xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c (revision 11262:b7ebfbf2359e)
13431Scarlsonj /*
23431Scarlsonj  * CDDL HEADER START
33431Scarlsonj  *
43431Scarlsonj  * The contents of this file are subject to the terms of the
53431Scarlsonj  * Common Development and Distribution License (the "License").
63431Scarlsonj  * You may not use this file except in compliance with the License.
73431Scarlsonj  *
83431Scarlsonj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93431Scarlsonj  * or http://www.opensolaris.org/os/licensing.
103431Scarlsonj  * See the License for the specific language governing permissions
113431Scarlsonj  * and limitations under the License.
123431Scarlsonj  *
133431Scarlsonj  * When distributing Covered Code, include this CDDL HEADER in each
143431Scarlsonj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153431Scarlsonj  * If applicable, add the following below this CDDL HEADER, with the
163431Scarlsonj  * fields enclosed by brackets "[]" replaced with your own identifying
173431Scarlsonj  * information: Portions Copyright [yyyy] [name of copyright owner]
183431Scarlsonj  *
193431Scarlsonj  * CDDL HEADER END
203431Scarlsonj  */
213431Scarlsonj 
223431Scarlsonj /*
23*11262SRajagopal.Andra@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
243431Scarlsonj  * Use is subject to license terms.
253431Scarlsonj  */
263431Scarlsonj 
273431Scarlsonj /*
283431Scarlsonj  * Dynamic Host Configuration Protocol version 6, for IPv6.  Supports
293431Scarlsonj  * RFCs 3315, 3319, 3646, 3898, 4075, 4242, 4280, 4580, 4649, and 4704.
303431Scarlsonj  */
313431Scarlsonj 
323431Scarlsonj #include <stdio.h>
333431Scarlsonj #include <stdlib.h>
343431Scarlsonj #include <string.h>
353431Scarlsonj #include <time.h>
363431Scarlsonj #include <sys/types.h>
373431Scarlsonj #include <sys/socket.h>
383431Scarlsonj #include <netinet/in.h>
393431Scarlsonj #include <netinet/dhcp6.h>
403431Scarlsonj #include <arpa/inet.h>
413431Scarlsonj #include <dhcp_impl.h>
423431Scarlsonj #include <dhcp_inittab.h>
433431Scarlsonj 
443431Scarlsonj #include "snoop.h"
453431Scarlsonj 
463431Scarlsonj static const char *mtype_to_str(uint8_t);
473431Scarlsonj static const char *option_to_str(uint8_t);
483431Scarlsonj static const char *duidtype_to_str(uint16_t);
493431Scarlsonj static const char *status_to_str(uint16_t);
503431Scarlsonj static const char *entr_to_str(uint32_t);
513431Scarlsonj static const char *reconf_to_str(uint8_t);
523431Scarlsonj static const char *authproto_to_str(uint8_t);
533431Scarlsonj static const char *authalg_to_str(uint8_t, uint8_t);
543431Scarlsonj static const char *authrdm_to_str(uint8_t);
553431Scarlsonj static const char *cwhat_to_str(uint8_t);
563431Scarlsonj static const char *catype_to_str(uint8_t);
573431Scarlsonj static void show_hex(const uint8_t *, int, const char *);
583431Scarlsonj static void show_ascii(const uint8_t *, int, const char *);
593431Scarlsonj static void show_address(const char *, const void *);
603431Scarlsonj static void show_options(const uint8_t *, int);
613431Scarlsonj 
623431Scarlsonj int
interpret_dhcpv6(int flags,const uint8_t * data,int len)633431Scarlsonj interpret_dhcpv6(int flags, const uint8_t *data, int len)
643431Scarlsonj {
653431Scarlsonj 	int olen = len;
663431Scarlsonj 	char *line, *lstart;
673431Scarlsonj 	dhcpv6_relay_t d6r;
683431Scarlsonj 	dhcpv6_message_t d6m;
693431Scarlsonj 	uint_t optlen;
703431Scarlsonj 	uint16_t statuscode;
713431Scarlsonj 
723431Scarlsonj 	if (len <= 0) {
733431Scarlsonj 		(void) strlcpy(get_sum_line(), "DHCPv6?", MAXLINE);
743431Scarlsonj 		return (0);
753431Scarlsonj 	}
763431Scarlsonj 	if (flags & F_SUM) {
773431Scarlsonj 		uint_t ias;
783431Scarlsonj 		dhcpv6_option_t *d6o;
793431Scarlsonj 		in6_addr_t link, peer;
803431Scarlsonj 		char linkstr[INET6_ADDRSTRLEN];
813431Scarlsonj 		char peerstr[INET6_ADDRSTRLEN];
823431Scarlsonj 
833431Scarlsonj 		line = lstart = get_sum_line();
843431Scarlsonj 		line += snprintf(line, MAXLINE, "DHCPv6 %s",
853431Scarlsonj 		    mtype_to_str(data[0]));
863431Scarlsonj 		if (data[0] == DHCPV6_MSG_RELAY_FORW ||
873431Scarlsonj 		    data[0] == DHCPV6_MSG_RELAY_REPL) {
883431Scarlsonj 			if (len < sizeof (d6r)) {
893431Scarlsonj 				(void) strlcpy(line, "?",
903431Scarlsonj 				    MAXLINE - (line - lstart));
913431Scarlsonj 				return (olen);
923431Scarlsonj 			}
933431Scarlsonj 			/* Not much in DHCPv6 is aligned. */
943431Scarlsonj 			(void) memcpy(&d6r, data, sizeof (d6r));
953431Scarlsonj 			(void) memcpy(&link, d6r.d6r_linkaddr, sizeof (link));
963431Scarlsonj 			(void) memcpy(&peer, d6r.d6r_peeraddr, sizeof (peer));
973431Scarlsonj 			line += snprintf(line, MAXLINE - (line - lstart),
983431Scarlsonj 			    " HC=%d link=%s peer=%s", d6r.d6r_hop_count,
993431Scarlsonj 			    inet_ntop(AF_INET6, &link, linkstr,
1003431Scarlsonj 			    sizeof (linkstr)),
1013431Scarlsonj 			    inet_ntop(AF_INET6, &peer, peerstr,
1023431Scarlsonj 			    sizeof (peerstr)));
1033431Scarlsonj 			data += sizeof (d6r);
1043431Scarlsonj 			len -= sizeof (d6r);
1053431Scarlsonj 		} else {
1063431Scarlsonj 			if (len < sizeof (d6m)) {
1073431Scarlsonj 				(void) strlcpy(line, "?",
1083431Scarlsonj 				    MAXLINE - (line - lstart));
1093431Scarlsonj 				return (olen);
1103431Scarlsonj 			}
1113431Scarlsonj 			(void) memcpy(&d6m, data, sizeof (d6m));
1123431Scarlsonj 			line += snprintf(line, MAXLINE - (line - lstart),
1133431Scarlsonj 			    " xid=%x", DHCPV6_GET_TRANSID(&d6m));
1143431Scarlsonj 			data += sizeof (d6m);
1153431Scarlsonj 			len -= sizeof (d6m);
1163431Scarlsonj 		}
1173431Scarlsonj 		ias = 0;
1183431Scarlsonj 		d6o = NULL;
1193431Scarlsonj 		while ((d6o = dhcpv6_find_option(data, len, d6o,
1203431Scarlsonj 		    DHCPV6_OPT_IA_NA, NULL)) != NULL)
1213431Scarlsonj 			ias++;
1223431Scarlsonj 		if (ias > 0)
1233431Scarlsonj 			line += snprintf(line, MAXLINE - (line - lstart),
1243431Scarlsonj 			    " IAs=%u", ias);
1253431Scarlsonj 		d6o = dhcpv6_find_option(data, len, NULL,
1263431Scarlsonj 		    DHCPV6_OPT_STATUS_CODE, &optlen);
1273431Scarlsonj 		optlen -= sizeof (*d6o);
1283431Scarlsonj 		if (d6o != NULL && optlen >= sizeof (statuscode)) {
1293431Scarlsonj 			(void) memcpy(&statuscode, d6o + 1,
1303431Scarlsonj 			    sizeof (statuscode));
1313431Scarlsonj 			line += snprintf(line, MAXLINE - (line - lstart),
1323431Scarlsonj 			    " status=%u", ntohs(statuscode));
1333431Scarlsonj 			optlen -= sizeof (statuscode);
1343431Scarlsonj 			if (optlen > 0) {
1353431Scarlsonj 				line += snprintf(line,
1363431Scarlsonj 				    MAXLINE - (line - lstart), " \"%.*s\"",
1373431Scarlsonj 				    optlen, (char *)(d6o + 1) + 2);
1383431Scarlsonj 			}
1393431Scarlsonj 		}
1403431Scarlsonj 		d6o = dhcpv6_find_option(data, len, NULL,
1413431Scarlsonj 		    DHCPV6_OPT_RELAY_MSG, &optlen);
1423431Scarlsonj 		optlen -= sizeof (*d6o);
1433431Scarlsonj 		if (d6o != NULL && optlen >= 1) {
1443431Scarlsonj 			line += snprintf(line, MAXLINE - (line - lstart),
1453431Scarlsonj 			    " relay=%s", mtype_to_str(*(uint8_t *)(d6o + 1)));
1463431Scarlsonj 		}
1473431Scarlsonj 	} else if (flags & F_DTAIL) {
1483431Scarlsonj 		show_header("DHCPv6: ",
1493431Scarlsonj 		    "Dynamic Host Configuration Protocol Version 6", len);
1503431Scarlsonj 		show_space();
1513431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(),
1523431Scarlsonj 		    "Message type (msg-type) = %u (%s)", data[0],
1533431Scarlsonj 		    mtype_to_str(data[0]));
1543431Scarlsonj 		if (data[0] == DHCPV6_MSG_RELAY_FORW ||
1553431Scarlsonj 		    data[0] == DHCPV6_MSG_RELAY_REPL) {
1563431Scarlsonj 			if (len < sizeof (d6r)) {
1573431Scarlsonj 				(void) strlcpy(get_line(0, 0), "Truncated",
1583431Scarlsonj 				    get_line_remain());
1593431Scarlsonj 				return (olen);
1603431Scarlsonj 			}
1613431Scarlsonj 			(void) memcpy(&d6r, data, sizeof (d6r));
1623431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
1633431Scarlsonj 			    "Hop count = %u", d6r.d6r_hop_count);
1643431Scarlsonj 			show_address("Link address", d6r.d6r_linkaddr);
1653431Scarlsonj 			show_address("Peer address", d6r.d6r_peeraddr);
1663431Scarlsonj 			data += sizeof (d6r);
1673431Scarlsonj 			len -= sizeof (d6r);
1683431Scarlsonj 		} else {
1693431Scarlsonj 			if (len < sizeof (d6m)) {
1703431Scarlsonj 				(void) strlcpy(get_line(0, 0), "Truncated",
1713431Scarlsonj 				    get_line_remain());
1723431Scarlsonj 				return (olen);
1733431Scarlsonj 			}
1743431Scarlsonj 			(void) memcpy(&d6m, data, sizeof (d6m));
1753431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
1763431Scarlsonj 			    "Transaction ID = %x", DHCPV6_GET_TRANSID(&d6m));
1773431Scarlsonj 			data += sizeof (d6m);
1783431Scarlsonj 			len -= sizeof (d6m);
1793431Scarlsonj 		}
1803431Scarlsonj 		show_space();
1813431Scarlsonj 		show_options(data, len);
1823431Scarlsonj 		show_space();
1833431Scarlsonj 	}
1843431Scarlsonj 	return (olen);
1853431Scarlsonj }
1863431Scarlsonj 
1873431Scarlsonj static const char *
mtype_to_str(uint8_t mtype)1883431Scarlsonj mtype_to_str(uint8_t mtype)
1893431Scarlsonj {
1903431Scarlsonj 	switch (mtype) {
1913431Scarlsonj 	case DHCPV6_MSG_SOLICIT:
1923431Scarlsonj 		return ("Solicit");
1933431Scarlsonj 	case DHCPV6_MSG_ADVERTISE:
1943431Scarlsonj 		return ("Advertise");
1953431Scarlsonj 	case DHCPV6_MSG_REQUEST:
1963431Scarlsonj 		return ("Request");
1973431Scarlsonj 	case DHCPV6_MSG_CONFIRM:
1983431Scarlsonj 		return ("Confirm");
1993431Scarlsonj 	case DHCPV6_MSG_RENEW:
2003431Scarlsonj 		return ("Renew");
2013431Scarlsonj 	case DHCPV6_MSG_REBIND:
2023431Scarlsonj 		return ("Rebind");
2033431Scarlsonj 	case DHCPV6_MSG_REPLY:
2043431Scarlsonj 		return ("Reply");
2053431Scarlsonj 	case DHCPV6_MSG_RELEASE:
2063431Scarlsonj 		return ("Release");
2073431Scarlsonj 	case DHCPV6_MSG_DECLINE:
2083431Scarlsonj 		return ("Decline");
2093431Scarlsonj 	case DHCPV6_MSG_RECONFIGURE:
2103431Scarlsonj 		return ("Reconfigure");
2113431Scarlsonj 	case DHCPV6_MSG_INFO_REQ:
2123431Scarlsonj 		return ("Information-Request");
2133431Scarlsonj 	case DHCPV6_MSG_RELAY_FORW:
2143431Scarlsonj 		return ("Relay-Forward");
2153431Scarlsonj 	case DHCPV6_MSG_RELAY_REPL:
2163431Scarlsonj 		return ("Relay-Reply");
2173431Scarlsonj 	default:
2183431Scarlsonj 		return ("Unknown");
2193431Scarlsonj 	}
2203431Scarlsonj }
2213431Scarlsonj 
2223431Scarlsonj static const char *
option_to_str(uint8_t mtype)2233431Scarlsonj option_to_str(uint8_t mtype)
2243431Scarlsonj {
2253431Scarlsonj 	switch (mtype) {
2263431Scarlsonj 	case DHCPV6_OPT_CLIENTID:
2273431Scarlsonj 		return ("Client Identifier");
2283431Scarlsonj 	case DHCPV6_OPT_SERVERID:
2293431Scarlsonj 		return ("Server Identifier");
2303431Scarlsonj 	case DHCPV6_OPT_IA_NA:
2313431Scarlsonj 		return ("Identity Association for Non-temporary Addresses");
2323431Scarlsonj 	case DHCPV6_OPT_IA_TA:
2333431Scarlsonj 		return ("Identity Association for Temporary Addresses");
2343431Scarlsonj 	case DHCPV6_OPT_IAADDR:
2353431Scarlsonj 		return ("IA Address");
2363431Scarlsonj 	case DHCPV6_OPT_ORO:
2373431Scarlsonj 		return ("Option Request");
2383431Scarlsonj 	case DHCPV6_OPT_PREFERENCE:
2393431Scarlsonj 		return ("Preference");
2403431Scarlsonj 	case DHCPV6_OPT_ELAPSED_TIME:
2413431Scarlsonj 		return ("Elapsed Time");
2423431Scarlsonj 	case DHCPV6_OPT_RELAY_MSG:
2433431Scarlsonj 		return ("Relay Message");
2443431Scarlsonj 	case DHCPV6_OPT_AUTH:
2453431Scarlsonj 		return ("Authentication");
2463431Scarlsonj 	case DHCPV6_OPT_UNICAST:
2473431Scarlsonj 		return ("Server Unicast");
2483431Scarlsonj 	case DHCPV6_OPT_STATUS_CODE:
2493431Scarlsonj 		return ("Status Code");
2503431Scarlsonj 	case DHCPV6_OPT_RAPID_COMMIT:
2513431Scarlsonj 		return ("Rapid Commit");
2523431Scarlsonj 	case DHCPV6_OPT_USER_CLASS:
2533431Scarlsonj 		return ("User Class");
2543431Scarlsonj 	case DHCPV6_OPT_VENDOR_CLASS:
2553431Scarlsonj 		return ("Vendor Class");
2563431Scarlsonj 	case DHCPV6_OPT_VENDOR_OPT:
2573431Scarlsonj 		return ("Vendor-specific Information");
2583431Scarlsonj 	case DHCPV6_OPT_INTERFACE_ID:
2593431Scarlsonj 		return ("Interface-Id");
2603431Scarlsonj 	case DHCPV6_OPT_RECONF_MSG:
2613431Scarlsonj 		return ("Reconfigure Message");
2623431Scarlsonj 	case DHCPV6_OPT_RECONF_ACC:
2633431Scarlsonj 		return ("Reconfigure Accept");
2643431Scarlsonj 	case DHCPV6_OPT_SIP_NAMES:
2653431Scarlsonj 		return ("SIP Servers Domain Name List");
2663431Scarlsonj 	case DHCPV6_OPT_SIP_ADDR:
2673431Scarlsonj 		return ("SIP Servers IPv6 Address List");
2683431Scarlsonj 	case DHCPV6_OPT_DNS_ADDR:
2693431Scarlsonj 		return ("DNS Recursive Name Server");
2703431Scarlsonj 	case DHCPV6_OPT_DNS_SEARCH:
2713431Scarlsonj 		return ("Domain Search List");
2723431Scarlsonj 	case DHCPV6_OPT_IA_PD:
2733431Scarlsonj 		return ("Identity Association for Prefix Delegation");
2743431Scarlsonj 	case DHCPV6_OPT_IAPREFIX:
2753431Scarlsonj 		return ("IA_PD Prefix");
2763431Scarlsonj 	case DHCPV6_OPT_NIS_SERVERS:
2773431Scarlsonj 		return ("Network Information Service Servers");
2783431Scarlsonj 	case DHCPV6_OPT_NIS_DOMAIN:
2793431Scarlsonj 		return ("Network Information Service Domain Name");
2803431Scarlsonj 	case DHCPV6_OPT_SNTP_SERVERS:
2813431Scarlsonj 		return ("Simple Network Time Protocol Servers");
2823431Scarlsonj 	case DHCPV6_OPT_INFO_REFTIME:
2833431Scarlsonj 		return ("Information Refresh Time");
2843431Scarlsonj 	case DHCPV6_OPT_BCMCS_SRV_D:
2853431Scarlsonj 		return ("BCMCS Controller Domain Name List");
2863431Scarlsonj 	case DHCPV6_OPT_BCMCS_SRV_A:
2873431Scarlsonj 		return ("BCMCS Controller IPv6 Address");
2883431Scarlsonj 	case DHCPV6_OPT_GEOCONF_CVC:
2893431Scarlsonj 		return ("Civic Location");
2903431Scarlsonj 	case DHCPV6_OPT_REMOTE_ID:
2913431Scarlsonj 		return ("Relay Agent Remote-ID");
2923431Scarlsonj 	case DHCPV6_OPT_SUBSCRIBER:
2933431Scarlsonj 		return ("Relay Agent Subscriber-ID");
2943431Scarlsonj 	case DHCPV6_OPT_CLIENT_FQDN:
2953431Scarlsonj 		return ("Client FQDN");
2963431Scarlsonj 	default:
2973431Scarlsonj 		return ("Unknown");
2983431Scarlsonj 	}
2993431Scarlsonj }
3003431Scarlsonj 
3013431Scarlsonj static const char *
duidtype_to_str(uint16_t dtype)3023431Scarlsonj duidtype_to_str(uint16_t dtype)
3033431Scarlsonj {
3043431Scarlsonj 	switch (dtype) {
3053431Scarlsonj 	case DHCPV6_DUID_LLT:
3063431Scarlsonj 		return ("Link-layer Address Plus Time");
3073431Scarlsonj 	case DHCPV6_DUID_EN:
3083431Scarlsonj 		return ("Enterprise Number");
3093431Scarlsonj 	case DHCPV6_DUID_LL:
3103431Scarlsonj 		return ("Link-layer Address");
3113431Scarlsonj 	default:
3123431Scarlsonj 		return ("Unknown");
3133431Scarlsonj 	}
3143431Scarlsonj }
3153431Scarlsonj 
3163431Scarlsonj static const char *
status_to_str(uint16_t status)3173431Scarlsonj status_to_str(uint16_t status)
3183431Scarlsonj {
3193431Scarlsonj 	switch (status) {
3203431Scarlsonj 	case DHCPV6_STAT_SUCCESS:
3213431Scarlsonj 		return ("Success");
3223431Scarlsonj 	case DHCPV6_STAT_UNSPECFAIL:
3233431Scarlsonj 		return ("Failure, reason unspecified");
3243431Scarlsonj 	case DHCPV6_STAT_NOADDRS:
3253431Scarlsonj 		return ("No addresses for IAs");
3263431Scarlsonj 	case DHCPV6_STAT_NOBINDING:
3273431Scarlsonj 		return ("Client binding unavailable");
3283431Scarlsonj 	case DHCPV6_STAT_NOTONLINK:
3293431Scarlsonj 		return ("Prefix not on link");
3303431Scarlsonj 	case DHCPV6_STAT_USEMCAST:
3313431Scarlsonj 		return ("Use multicast");
3323431Scarlsonj 	case DHCPV6_STAT_NOPREFIX:
3333431Scarlsonj 		return ("No prefix available");
3343431Scarlsonj 	default:
3353431Scarlsonj 		return ("Unknown");
3363431Scarlsonj 	}
3373431Scarlsonj }
3383431Scarlsonj 
3393431Scarlsonj static const char *
entr_to_str(uint32_t entr)3403431Scarlsonj entr_to_str(uint32_t entr)
3413431Scarlsonj {
3423431Scarlsonj 	switch (entr) {
3433431Scarlsonj 	case DHCPV6_SUN_ENT:
3443431Scarlsonj 		return ("Sun Microsystems");
3453431Scarlsonj 	default:
3463431Scarlsonj 		return ("Unknown");
3473431Scarlsonj 	}
3483431Scarlsonj }
3493431Scarlsonj 
3503431Scarlsonj static const char *
reconf_to_str(uint8_t msgtype)3513431Scarlsonj reconf_to_str(uint8_t msgtype)
3523431Scarlsonj {
3533431Scarlsonj 	switch (msgtype) {
3543431Scarlsonj 	case DHCPV6_RECONF_RENEW:
3553431Scarlsonj 		return ("Renew");
3563431Scarlsonj 	case DHCPV6_RECONF_INFO:
3573431Scarlsonj 		return ("Information-request");
3583431Scarlsonj 	default:
3593431Scarlsonj 		return ("Unknown");
3603431Scarlsonj 	}
3613431Scarlsonj }
3623431Scarlsonj 
3633431Scarlsonj static const char *
authproto_to_str(uint8_t aproto)3643431Scarlsonj authproto_to_str(uint8_t aproto)
3653431Scarlsonj {
3663431Scarlsonj 	switch (aproto) {
3673431Scarlsonj 	case DHCPV6_PROTO_DELAYED:
3683431Scarlsonj 		return ("Delayed");
3693431Scarlsonj 	case DHCPV6_PROTO_RECONFIG:
3703431Scarlsonj 		return ("Reconfigure Key");
3713431Scarlsonj 	default:
3723431Scarlsonj 		return ("Unknown");
3733431Scarlsonj 	}
3743431Scarlsonj }
3753431Scarlsonj 
3763431Scarlsonj static const char *
authalg_to_str(uint8_t aproto,uint8_t aalg)3773431Scarlsonj authalg_to_str(uint8_t aproto, uint8_t aalg)
3783431Scarlsonj {
3793431Scarlsonj 	switch (aproto) {
3803431Scarlsonj 	case DHCPV6_PROTO_DELAYED:
3813431Scarlsonj 	case DHCPV6_PROTO_RECONFIG:
3823431Scarlsonj 		switch (aalg) {
3833431Scarlsonj 		case DHCPV6_ALG_HMAC_MD5:
3843431Scarlsonj 			return ("HMAC-MD5 Signature");
3853431Scarlsonj 		default:
3863431Scarlsonj 			return ("Unknown");
3873431Scarlsonj 		}
3883431Scarlsonj 		break;
3893431Scarlsonj 	default:
3903431Scarlsonj 		return ("Unknown");
3913431Scarlsonj 	}
3923431Scarlsonj }
3933431Scarlsonj 
3943431Scarlsonj static const char *
authrdm_to_str(uint8_t ardm)3953431Scarlsonj authrdm_to_str(uint8_t ardm)
3963431Scarlsonj {
3973431Scarlsonj 	switch (ardm) {
3983431Scarlsonj 	case DHCPV6_RDM_MONOCNT:
3993431Scarlsonj 		return ("Monotonic Counter");
4003431Scarlsonj 	default:
4013431Scarlsonj 		return ("Unknown");
4023431Scarlsonj 	}
4033431Scarlsonj }
4043431Scarlsonj 
4053431Scarlsonj static const char *
cwhat_to_str(uint8_t what)4063431Scarlsonj cwhat_to_str(uint8_t what)
4073431Scarlsonj {
4083431Scarlsonj 	switch (what) {
4093431Scarlsonj 	case DHCPV6_CWHAT_SERVER:
4103431Scarlsonj 		return ("Server");
4113431Scarlsonj 	case DHCPV6_CWHAT_NETWORK:
4123431Scarlsonj 		return ("Network");
4133431Scarlsonj 	case DHCPV6_CWHAT_CLIENT:
4143431Scarlsonj 		return ("Client");
4153431Scarlsonj 	default:
4163431Scarlsonj 		return ("Unknown");
4173431Scarlsonj 	}
4183431Scarlsonj }
4193431Scarlsonj 
4203431Scarlsonj static const char *
catype_to_str(uint8_t catype)4213431Scarlsonj catype_to_str(uint8_t catype)
4223431Scarlsonj {
4233431Scarlsonj 	switch (catype) {
4243431Scarlsonj 	case CIVICADDR_LANG:
4253431Scarlsonj 		return ("Language; RFC 2277");
4263431Scarlsonj 	case CIVICADDR_A1:
4273431Scarlsonj 		return ("National division (state)");
4283431Scarlsonj 	case CIVICADDR_A2:
4293431Scarlsonj 		return ("County");
4303431Scarlsonj 	case CIVICADDR_A3:
4313431Scarlsonj 		return ("City");
4323431Scarlsonj 	case CIVICADDR_A4:
4333431Scarlsonj 		return ("City division");
4343431Scarlsonj 	case CIVICADDR_A5:
4353431Scarlsonj 		return ("Neighborhood");
4363431Scarlsonj 	case CIVICADDR_A6:
4373431Scarlsonj 		return ("Street group");
4383431Scarlsonj 	case CIVICADDR_PRD:
4393431Scarlsonj 		return ("Leading street direction");
4403431Scarlsonj 	case CIVICADDR_POD:
4413431Scarlsonj 		return ("Trailing street suffix");
4423431Scarlsonj 	case CIVICADDR_STS:
4433431Scarlsonj 		return ("Street suffix or type");
4443431Scarlsonj 	case CIVICADDR_HNO:
4453431Scarlsonj 		return ("House number");
4463431Scarlsonj 	case CIVICADDR_HNS:
4473431Scarlsonj 		return ("House number suffix");
4483431Scarlsonj 	case CIVICADDR_LMK:
4493431Scarlsonj 		return ("Landmark");
4503431Scarlsonj 	case CIVICADDR_LOC:
4513431Scarlsonj 		return ("Additional location information");
4523431Scarlsonj 	case CIVICADDR_NAM:
4533431Scarlsonj 		return ("Name/occupant");
4543431Scarlsonj 	case CIVICADDR_PC:
4553431Scarlsonj 		return ("Postal Code/ZIP");
4563431Scarlsonj 	case CIVICADDR_BLD:
4573431Scarlsonj 		return ("Building");
4583431Scarlsonj 	case CIVICADDR_UNIT:
4593431Scarlsonj 		return ("Unit/apt/suite");
4603431Scarlsonj 	case CIVICADDR_FLR:
4613431Scarlsonj 		return ("Floor");
4623431Scarlsonj 	case CIVICADDR_ROOM:
4633431Scarlsonj 		return ("Room number");
4643431Scarlsonj 	case CIVICADDR_TYPE:
4653431Scarlsonj 		return ("Place type");
4663431Scarlsonj 	case CIVICADDR_PCN:
4673431Scarlsonj 		return ("Postal community name");
4683431Scarlsonj 	case CIVICADDR_POBOX:
4693431Scarlsonj 		return ("Post office box");
4703431Scarlsonj 	case CIVICADDR_ADDL:
4713431Scarlsonj 		return ("Additional code");
4723431Scarlsonj 	case CIVICADDR_SEAT:
4733431Scarlsonj 		return ("Seat/desk");
4743431Scarlsonj 	case CIVICADDR_ROAD:
4753431Scarlsonj 		return ("Primary road or street");
4763431Scarlsonj 	case CIVICADDR_RSEC:
4773431Scarlsonj 		return ("Road section");
4783431Scarlsonj 	case CIVICADDR_RBRA:
4793431Scarlsonj 		return ("Road branch");
4803431Scarlsonj 	case CIVICADDR_RSBR:
4813431Scarlsonj 		return ("Road sub-branch");
4823431Scarlsonj 	case CIVICADDR_SPRE:
4833431Scarlsonj 		return ("Street name pre-modifier");
4843431Scarlsonj 	case CIVICADDR_SPOST:
4853431Scarlsonj 		return ("Street name post-modifier");
4863431Scarlsonj 	case CIVICADDR_SCRIPT:
4873431Scarlsonj 		return ("Script");
4883431Scarlsonj 	default:
4893431Scarlsonj 		return ("Unknown");
4903431Scarlsonj 	}
4913431Scarlsonj }
4923431Scarlsonj 
4933431Scarlsonj static void
show_hex(const uint8_t * data,int len,const char * name)4943431Scarlsonj show_hex(const uint8_t *data, int len, const char *name)
4953431Scarlsonj {
4963431Scarlsonj 	char buffer[16 * 3 + 1];
4973431Scarlsonj 	int nlen;
4983431Scarlsonj 	int i;
4993431Scarlsonj 	char sep;
5003431Scarlsonj 
5013431Scarlsonj 	nlen = strlen(name);
5023431Scarlsonj 	sep = '=';
5033431Scarlsonj 	while (len > 0) {
5043431Scarlsonj 		for (i = 0; i < 16 && i < len; i++)
5053431Scarlsonj 			(void) snprintf(buffer + 3 * i, 4, " %02x", *data++);
5063431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(), "%*s %c%s",
5073431Scarlsonj 		    nlen, name, sep, buffer);
5083431Scarlsonj 		name = "";
5093431Scarlsonj 		sep = ' ';
5103431Scarlsonj 		len -= i;
5113431Scarlsonj 	}
5123431Scarlsonj }
5133431Scarlsonj 
5143431Scarlsonj static void
show_ascii(const uint8_t * data,int len,const char * name)5153431Scarlsonj show_ascii(const uint8_t *data, int len, const char *name)
5163431Scarlsonj {
5173431Scarlsonj 	char buffer[64], *bp;
5183431Scarlsonj 	int nlen;
5193431Scarlsonj 	int i;
5203431Scarlsonj 	char sep;
5213431Scarlsonj 
5223431Scarlsonj 	nlen = strlen(name);
5233431Scarlsonj 	sep = '=';
5243431Scarlsonj 	while (len > 0) {
5253431Scarlsonj 		bp = buffer;
5263431Scarlsonj 		for (i = 0; i < sizeof (buffer) - 4 && len > 0; len--) {
5273431Scarlsonj 			if (!isascii(*data) || !isprint(*data))
5283431Scarlsonj 				bp += snprintf(bp, 5, "\\%03o", *data++);
5293431Scarlsonj 			else
5303431Scarlsonj 				*bp++;
5313431Scarlsonj 		}
5323431Scarlsonj 		*bp = '\0';
5333431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(),
5343431Scarlsonj 		    "%*s %c \"%s\"", nlen, name, sep, buffer);
5353431Scarlsonj 		sep = ' ';
5363431Scarlsonj 		name = "";
5373431Scarlsonj 	}
5383431Scarlsonj }
5393431Scarlsonj 
5403431Scarlsonj static void
show_address(const char * addrname,const void * aptr)5413431Scarlsonj show_address(const char *addrname, const void *aptr)
5423431Scarlsonj {
5433431Scarlsonj 	char *hname;
5443431Scarlsonj 	char addrstr[INET6_ADDRSTRLEN];
5453431Scarlsonj 	in6_addr_t addr;
5463431Scarlsonj 
5473431Scarlsonj 	(void) memcpy(&addr, aptr, sizeof (in6_addr_t));
5483431Scarlsonj 	(void) inet_ntop(AF_INET6, &addr, addrstr, sizeof (addrstr));
5493431Scarlsonj 	hname = addrtoname(AF_INET6, &addr);
5503431Scarlsonj 	if (strcmp(hname, addrstr) == 0) {
5513431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(), "%s = %s",
5523431Scarlsonj 		    addrname, addrstr);
5533431Scarlsonj 	} else {
5543431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(),
5553431Scarlsonj 		    "%s = %s (%s)", addrname, addrstr, hname);
5563431Scarlsonj 	}
5573431Scarlsonj }
5583431Scarlsonj 
5593431Scarlsonj static void
nest_options(const uint8_t * data,uint_t olen,char * prefix,char * title)5603431Scarlsonj nest_options(const uint8_t *data, uint_t olen, char *prefix, char *title)
5613431Scarlsonj {
5623431Scarlsonj 	char *str, *oldnest, *oldprefix;
5633431Scarlsonj 
5643431Scarlsonj 	if (olen <= 0)
5653431Scarlsonj 		return;
5663431Scarlsonj 	oldprefix = prot_prefix;
5673431Scarlsonj 	oldnest = prot_nest_prefix;
5683431Scarlsonj 	str = malloc(strlen(prot_nest_prefix) + strlen(prot_prefix) + 1);
5693431Scarlsonj 	if (str == NULL) {
5703431Scarlsonj 		prot_nest_prefix = prot_prefix;
5713431Scarlsonj 	} else {
5723431Scarlsonj 		(void) sprintf(str, "%s%s", prot_nest_prefix, prot_prefix);
5733431Scarlsonj 		prot_nest_prefix = str;
5743431Scarlsonj 	}
5753431Scarlsonj 	show_header(prefix, title, 0);
5763431Scarlsonj 	show_options(data, olen);
5773431Scarlsonj 	free(str);
5783431Scarlsonj 	prot_prefix = oldprefix;
5793431Scarlsonj 	prot_nest_prefix = oldnest;
5803431Scarlsonj }
5813431Scarlsonj 
5823431Scarlsonj static void
show_options(const uint8_t * data,int len)5833431Scarlsonj show_options(const uint8_t *data, int len)
5843431Scarlsonj {
5853431Scarlsonj 	dhcpv6_option_t d6o;
5863431Scarlsonj 	uint_t olen, retlen;
5873431Scarlsonj 	uint16_t val16;
5883431Scarlsonj 	uint16_t type;
5893431Scarlsonj 	uint32_t val32;
5903431Scarlsonj 	const uint8_t *ostart;
5913431Scarlsonj 	char *str, *sp;
5923431Scarlsonj 	char *oldnest;
5933431Scarlsonj 
5943431Scarlsonj 	/*
5953431Scarlsonj 	 * Be very careful with negative numbers; ANSI signed/unsigned
5963431Scarlsonj 	 * comparison doesn't work as expected.
5973431Scarlsonj 	 */
5983431Scarlsonj 	while (len >= (signed)sizeof (d6o)) {
5993431Scarlsonj 		(void) memcpy(&d6o, data, sizeof (d6o));
6003431Scarlsonj 		d6o.d6o_code = ntohs(d6o.d6o_code);
6013431Scarlsonj 		d6o.d6o_len = olen = ntohs(d6o.d6o_len);
6023431Scarlsonj 		(void) snprintf(get_line(0, 0), get_line_remain(),
6033431Scarlsonj 		    "Option Code = %u (%s)", d6o.d6o_code,
6043431Scarlsonj 		    option_to_str(d6o.d6o_code));
6053431Scarlsonj 		ostart = data += sizeof (d6o);
6063431Scarlsonj 		len -= sizeof (d6o);
6073431Scarlsonj 		if (olen > len) {
6083431Scarlsonj 			(void) strlcpy(get_line(0, 0), "Option truncated",
6093431Scarlsonj 			    get_line_remain());
6103431Scarlsonj 			olen = len;
6113431Scarlsonj 		}
6123431Scarlsonj 		switch (d6o.d6o_code) {
6133431Scarlsonj 		case DHCPV6_OPT_CLIENTID:
6143431Scarlsonj 		case DHCPV6_OPT_SERVERID:
6153431Scarlsonj 			if (olen < sizeof (val16))
6163431Scarlsonj 				break;
6173431Scarlsonj 			(void) memcpy(&val16, data, sizeof (val16));
6183431Scarlsonj 			data += sizeof (val16);
6193431Scarlsonj 			olen -= sizeof (val16);
6203431Scarlsonj 			type = ntohs(val16);
6213431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
6223431Scarlsonj 			    "  DUID Type = %u (%s)", type,
6233431Scarlsonj 			    duidtype_to_str(type));
6243431Scarlsonj 			if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
6253431Scarlsonj 				if (olen < sizeof (val16))
6263431Scarlsonj 					break;
6273431Scarlsonj 				(void) memcpy(&val16, data, sizeof (val16));
6283431Scarlsonj 				data += sizeof (val16);
6293431Scarlsonj 				olen -= sizeof (val16);
6303431Scarlsonj 				val16 = ntohs(val16);
6313431Scarlsonj 				(void) snprintf(get_line(0, 0),
6323431Scarlsonj 				    get_line_remain(),
6333431Scarlsonj 				    "  Hardware Type = %u (%s)", val16,
6343431Scarlsonj 				    arp_htype(type));
6353431Scarlsonj 			}
6363431Scarlsonj 			if (type == DHCPV6_DUID_LLT) {
6373431Scarlsonj 				time_t timevalue;
6383431Scarlsonj 
6393431Scarlsonj 				if (olen < sizeof (val32))
6403431Scarlsonj 					break;
6413431Scarlsonj 				(void) memcpy(&val32, data, sizeof (val32));
6423431Scarlsonj 				data += sizeof (val32);
6433431Scarlsonj 				olen -= sizeof (val32);
6443431Scarlsonj 				timevalue = ntohl(val32) + DUID_TIME_BASE;
6453431Scarlsonj 				(void) snprintf(get_line(0, 0),
6463431Scarlsonj 				    get_line_remain(),
6473431Scarlsonj 				    "  Time = %lu (%.24s)", ntohl(val32),
6483431Scarlsonj 				    ctime(&timevalue));
6493431Scarlsonj 			}
6503431Scarlsonj 			if (type == DHCPV6_DUID_EN) {
6513431Scarlsonj 				if (olen < sizeof (val32))
6523431Scarlsonj 					break;
6533431Scarlsonj 				(void) memcpy(&val32, data, sizeof (val32));
6543431Scarlsonj 				data += sizeof (val32);
6553431Scarlsonj 				olen -= sizeof (val32);
6563431Scarlsonj 				val32 = ntohl(val32);
6573431Scarlsonj 				(void) snprintf(get_line(0, 0),
6583431Scarlsonj 				    get_line_remain(),
6593431Scarlsonj 				    "  Enterprise Number = %lu (%s)", val32,
6603431Scarlsonj 				    entr_to_str(val32));
6613431Scarlsonj 			}
6623431Scarlsonj 			if (olen == 0)
6633431Scarlsonj 				break;
6643431Scarlsonj 			if ((str = malloc(olen * 3)) == NULL)
6653431Scarlsonj 				pr_err("interpret_dhcpv6: no mem");
6663431Scarlsonj 			sp = str + snprintf(str, 3, "%02x", *data++);
6673431Scarlsonj 			while (--olen > 0) {
6683431Scarlsonj 				*sp++ = (type == DHCPV6_DUID_LLT ||
6693431Scarlsonj 				    type == DHCPV6_DUID_LL) ? ':' : ' ';
6703431Scarlsonj 				sp = sp + snprintf(sp, 3, "%02x", *data++);
6713431Scarlsonj 			}
6723431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
6733431Scarlsonj 			    (type == DHCPV6_DUID_LLT ||
6743431Scarlsonj 			    type == DHCPV6_DUID_LL) ?
6753431Scarlsonj 			    "  Link Layer Address = %s" :
6763431Scarlsonj 			    "  Identifier = %s", str);
6773431Scarlsonj 			free(str);
6783431Scarlsonj 			break;
6793431Scarlsonj 		case DHCPV6_OPT_IA_NA:
6803431Scarlsonj 		case DHCPV6_OPT_IA_PD: {
6813431Scarlsonj 			dhcpv6_ia_na_t d6in;
6823431Scarlsonj 
6833431Scarlsonj 			if (olen < sizeof (d6in) - sizeof (d6o))
6843431Scarlsonj 				break;
6853431Scarlsonj 			(void) memcpy(&d6in, data - sizeof (d6o),
6863431Scarlsonj 			    sizeof (d6in));
6873431Scarlsonj 			data += sizeof (d6in) - sizeof (d6o);
6883431Scarlsonj 			olen -= sizeof (d6in) - sizeof (d6o);
6893431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
6903431Scarlsonj 			    "  IAID = %u", ntohl(d6in.d6in_iaid));
6913431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
6923431Scarlsonj 			    "  T1 (renew) = %u seconds", ntohl(d6in.d6in_t1));
6933431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
6943431Scarlsonj 			    "  T2 (rebind) = %u seconds", ntohl(d6in.d6in_t2));
6953431Scarlsonj 			nest_options(data, olen, "IA: ",
6963431Scarlsonj 			    "Identity Association");
6973431Scarlsonj 			break;
6983431Scarlsonj 		}
6993431Scarlsonj 		case DHCPV6_OPT_IA_TA: {
7003431Scarlsonj 			dhcpv6_ia_ta_t d6it;
7013431Scarlsonj 
7023431Scarlsonj 			if (olen < sizeof (d6it) - sizeof (d6o))
7033431Scarlsonj 				break;
7043431Scarlsonj 			(void) memcpy(&d6it, data - sizeof (d6o),
7053431Scarlsonj 			    sizeof (d6it));
7063431Scarlsonj 			data += sizeof (d6it) - sizeof (d6o);
7073431Scarlsonj 			olen -= sizeof (d6it) - sizeof (d6o);
7083431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7093431Scarlsonj 			    "  IAID = %u", ntohl(d6it.d6it_iaid));
7103431Scarlsonj 			nest_options(data, olen, "IA: ",
7113431Scarlsonj 			    "Identity Association");
7123431Scarlsonj 			break;
7133431Scarlsonj 		}
7143431Scarlsonj 		case DHCPV6_OPT_IAADDR: {
7153431Scarlsonj 			dhcpv6_iaaddr_t d6ia;
7163431Scarlsonj 
7173431Scarlsonj 			if (olen < sizeof (d6ia) - sizeof (d6o))
7183431Scarlsonj 				break;
7193431Scarlsonj 			(void) memcpy(&d6ia, data - sizeof (d6o),
7203431Scarlsonj 			    sizeof (d6ia));
7213431Scarlsonj 			data += sizeof (d6ia) - sizeof (d6o);
7223431Scarlsonj 			olen -= sizeof (d6ia) - sizeof (d6o);
7233431Scarlsonj 			show_address("  Address", &d6ia.d6ia_addr);
7243431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7253431Scarlsonj 			    "  Preferred lifetime = %u seconds",
7263431Scarlsonj 			    ntohl(d6ia.d6ia_preflife));
7273431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7283431Scarlsonj 			    "  Valid lifetime = %u seconds",
7293431Scarlsonj 			    ntohl(d6ia.d6ia_vallife));
7303431Scarlsonj 			nest_options(data, olen, "ADDR: ", "Address");
7313431Scarlsonj 			break;
7323431Scarlsonj 		}
7333431Scarlsonj 		case DHCPV6_OPT_ORO:
7343431Scarlsonj 			while (olen >= sizeof (val16)) {
7353431Scarlsonj 				(void) memcpy(&val16, data, sizeof (val16));
7363431Scarlsonj 				val16 = ntohs(val16);
7373431Scarlsonj 				(void) snprintf(get_line(0, 0),
7383431Scarlsonj 				    get_line_remain(),
7393431Scarlsonj 				    "  Requested Option Code = %u (%s)", val16,
7403431Scarlsonj 				    option_to_str(val16));
7413431Scarlsonj 				data += sizeof (val16);
7423431Scarlsonj 				olen -= sizeof (val16);
7433431Scarlsonj 			}
7443431Scarlsonj 			break;
7453431Scarlsonj 		case DHCPV6_OPT_PREFERENCE:
7463431Scarlsonj 			if (olen > 0) {
7473431Scarlsonj 				(void) snprintf(get_line(0, 0),
7483431Scarlsonj 				    get_line_remain(),
7493431Scarlsonj 				    *data == 255 ?
7503431Scarlsonj 				    "  Preference = %u (immediate)" :
7513431Scarlsonj 				    "  Preference = %u", *data);
7523431Scarlsonj 			}
7533431Scarlsonj 			break;
7543431Scarlsonj 		case DHCPV6_OPT_ELAPSED_TIME:
7553431Scarlsonj 			if (olen == sizeof (val16)) {
7563431Scarlsonj 				(void) memcpy(&val16, data, sizeof (val16));
7573431Scarlsonj 				val16 = ntohs(val16);
7583431Scarlsonj 				(void) snprintf(get_line(0, 0),
7593431Scarlsonj 				    get_line_remain(),
7603431Scarlsonj 				    "  Elapsed Time = %u.%02u seconds",
7613431Scarlsonj 				    val16 / 100, val16 % 100);
7623431Scarlsonj 			}
7633431Scarlsonj 			break;
7643431Scarlsonj 		case DHCPV6_OPT_RELAY_MSG:
7653431Scarlsonj 			if (olen > 0) {
7663431Scarlsonj 				oldnest = prot_nest_prefix;
7673431Scarlsonj 				prot_nest_prefix = prot_prefix;
7683431Scarlsonj 				retlen = interpret_dhcpv6(F_DTAIL, data, olen);
7693431Scarlsonj 				prot_prefix = prot_nest_prefix;
7703431Scarlsonj 				prot_nest_prefix = oldnest;
7713431Scarlsonj 			}
7723431Scarlsonj 			break;
7733431Scarlsonj 		case DHCPV6_OPT_AUTH: {
7743431Scarlsonj 			dhcpv6_auth_t d6a;
7753431Scarlsonj 
7763431Scarlsonj 			if (olen < DHCPV6_AUTH_SIZE - sizeof (d6o))
7773431Scarlsonj 				break;
7783431Scarlsonj 			(void) memcpy(&d6a, data - sizeof (d6o),
7793431Scarlsonj 			    DHCPV6_AUTH_SIZE);
7803431Scarlsonj 			data += DHCPV6_AUTH_SIZE - sizeof (d6o);
7813431Scarlsonj 			olen += DHCPV6_AUTH_SIZE - sizeof (d6o);
7823431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7833431Scarlsonj 			    "  Protocol = %u (%s)", d6a.d6a_proto,
7843431Scarlsonj 			    authproto_to_str(d6a.d6a_proto));
7853431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7863431Scarlsonj 			    "  Algorithm = %u (%s)", d6a.d6a_alg,
7873431Scarlsonj 			    authalg_to_str(d6a.d6a_proto, d6a.d6a_alg));
7883431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
7893431Scarlsonj 			    "  Replay Detection Method = %u (%s)", d6a.d6a_rdm,
7903431Scarlsonj 			    authrdm_to_str(d6a.d6a_rdm));
7913431Scarlsonj 			show_hex(d6a.d6a_replay, sizeof (d6a.d6a_replay),
7923431Scarlsonj 			    "  RDM Data");
7933431Scarlsonj 			if (olen > 0)
7943431Scarlsonj 				show_hex(data, olen, "  Auth Info");
7953431Scarlsonj 			break;
7963431Scarlsonj 		}
7973431Scarlsonj 		case DHCPV6_OPT_UNICAST:
7983431Scarlsonj 			if (olen >= sizeof (in6_addr_t))
7993431Scarlsonj 				show_address("  Server Address", data);
8003431Scarlsonj 			break;
8013431Scarlsonj 		case DHCPV6_OPT_STATUS_CODE:
8023431Scarlsonj 			if (olen < sizeof (val16))
8033431Scarlsonj 				break;
8043431Scarlsonj 			(void) memcpy(&val16, data, sizeof (val16));
8053431Scarlsonj 			val16 = ntohs(val16);
8063431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
8073431Scarlsonj 			    "  Status Code = %u (%s)", val16,
8083431Scarlsonj 			    status_to_str(val16));
8093431Scarlsonj 			data += sizeof (val16);
8103431Scarlsonj 			olen -= sizeof (val16);
8113431Scarlsonj 			if (olen > 0)
8123431Scarlsonj 				(void) snprintf(get_line(0, 0),
8133431Scarlsonj 				    get_line_remain(), "  Text = \"%.*s\"",
8143431Scarlsonj 				    olen, data);
8153431Scarlsonj 			break;
8163431Scarlsonj 		case DHCPV6_OPT_VENDOR_CLASS:
8173431Scarlsonj 			if (olen < sizeof (val32))
8183431Scarlsonj 				break;
8193431Scarlsonj 			(void) memcpy(&val32, data, sizeof (val32));
8203431Scarlsonj 			data += sizeof (val32);
8213431Scarlsonj 			olen -= sizeof (val32);
8223431Scarlsonj 			val32 = ntohl(val32);
8233431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
8243431Scarlsonj 			    "  Enterprise Number = %lu (%s)", val32,
8253431Scarlsonj 			    entr_to_str(val32));
8263431Scarlsonj 			/* FALLTHROUGH */
8273431Scarlsonj 		case DHCPV6_OPT_USER_CLASS:
8283431Scarlsonj 			while (olen >= sizeof (val16)) {
8293431Scarlsonj 				(void) memcpy(&val16, data, sizeof (val16));
8303431Scarlsonj 				data += sizeof (val16);
8313431Scarlsonj 				olen -= sizeof (val16);
8323431Scarlsonj 				val16 = ntohs(val16);
8333431Scarlsonj 				if (val16 > olen) {
8343431Scarlsonj 					(void) strlcpy(get_line(0, 0),
8353431Scarlsonj 					    "  Truncated class",
8363431Scarlsonj 					    get_line_remain());
8373431Scarlsonj 					val16 = olen;
8383431Scarlsonj 				}
8393431Scarlsonj 				show_hex(data, olen, "  Class");
8403431Scarlsonj 				data += val16;
8413431Scarlsonj 				olen -= val16;
8423431Scarlsonj 			}
8433431Scarlsonj 			break;
8443431Scarlsonj 		case DHCPV6_OPT_VENDOR_OPT: {
8453431Scarlsonj 			dhcpv6_option_t sd6o;
8463431Scarlsonj 
8473431Scarlsonj 			if (olen < sizeof (val32))
8483431Scarlsonj 				break;
8493431Scarlsonj 			(void) memcpy(&val32, data, sizeof (val32));
8503431Scarlsonj 			data += sizeof (val32);
8513431Scarlsonj 			olen -= sizeof (val32);
8523431Scarlsonj 			val32 = ntohl(val32);
8533431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
8543431Scarlsonj 			    "  Enterprise Number = %lu (%s)", val32,
8553431Scarlsonj 			    entr_to_str(val32));
8563431Scarlsonj 			while (olen >= sizeof (sd6o)) {
8573431Scarlsonj 				(void) memcpy(&sd6o, data, sizeof (sd6o));
8583431Scarlsonj 				sd6o.d6o_code = ntohs(sd6o.d6o_code);
8593431Scarlsonj 				sd6o.d6o_len = ntohs(sd6o.d6o_len);
8603431Scarlsonj 				(void) snprintf(get_line(0, 0),
8613431Scarlsonj 				    get_line_remain(),
8623431Scarlsonj 				    "  Vendor Option Code = %u", d6o.d6o_code);
8633431Scarlsonj 				data += sizeof (d6o);
8643431Scarlsonj 				olen -= sizeof (d6o);
8653431Scarlsonj 				if (sd6o.d6o_len > olen) {
8663431Scarlsonj 					(void) strlcpy(get_line(0, 0),
8673431Scarlsonj 					    "  Vendor Option truncated",
8683431Scarlsonj 					    get_line_remain());
8693431Scarlsonj 					sd6o.d6o_len = olen;
8703431Scarlsonj 				}
8713431Scarlsonj 				if (sd6o.d6o_len > 0) {
8723431Scarlsonj 					show_hex(data, sd6o.d6o_len,
8733431Scarlsonj 					    "    Data");
8743431Scarlsonj 					data += sd6o.d6o_len;
8753431Scarlsonj 					olen -= sd6o.d6o_len;
8763431Scarlsonj 				}
8773431Scarlsonj 			}
8783431Scarlsonj 			break;
8793431Scarlsonj 		}
8803431Scarlsonj 		case DHCPV6_OPT_REMOTE_ID:
8813431Scarlsonj 			if (olen < sizeof (val32))
8823431Scarlsonj 				break;
8833431Scarlsonj 			(void) memcpy(&val32, data, sizeof (val32));
8843431Scarlsonj 			data += sizeof (val32);
8853431Scarlsonj 			olen -= sizeof (val32);
8863431Scarlsonj 			val32 = ntohl(val32);
8873431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
8883431Scarlsonj 			    "  Enterprise Number = %lu (%s)", val32,
8893431Scarlsonj 			    entr_to_str(val32));
8903431Scarlsonj 			/* FALLTHROUGH */
8913431Scarlsonj 		case DHCPV6_OPT_INTERFACE_ID:
8923431Scarlsonj 		case DHCPV6_OPT_SUBSCRIBER:
8933431Scarlsonj 			if (olen > 0)
8943431Scarlsonj 				show_hex(data, olen, "  ID");
8953431Scarlsonj 			break;
8963431Scarlsonj 		case DHCPV6_OPT_RECONF_MSG:
8973431Scarlsonj 			if (olen > 0) {
8983431Scarlsonj 				(void) snprintf(get_line(0, 0),
8993431Scarlsonj 				    get_line_remain(),
9003431Scarlsonj 				    "  Message Type = %u (%s)", *data,
9013431Scarlsonj 				    reconf_to_str(*data));
9023431Scarlsonj 			}
9033431Scarlsonj 			break;
9043431Scarlsonj 		case DHCPV6_OPT_SIP_NAMES:
9053431Scarlsonj 		case DHCPV6_OPT_DNS_SEARCH:
9063431Scarlsonj 		case DHCPV6_OPT_NIS_DOMAIN:
9073431Scarlsonj 		case DHCPV6_OPT_BCMCS_SRV_D: {
9083431Scarlsonj 			dhcp_symbol_t *symp;
9093431Scarlsonj 			char *sp2;
9103431Scarlsonj 
9113431Scarlsonj 			symp = inittab_getbycode(
9123431Scarlsonj 			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
9133431Scarlsonj 			    d6o.d6o_code);
9143431Scarlsonj 			if (symp != NULL) {
9153431Scarlsonj 				str = inittab_decode(symp, data, olen, B_TRUE);
9163431Scarlsonj 				if (str != NULL) {
9173431Scarlsonj 					sp = str;
9183431Scarlsonj 					do {
9193431Scarlsonj 						sp2 = strchr(sp, ' ');
9203431Scarlsonj 						if (sp2 != NULL)
9213431Scarlsonj 							*sp2++ = '\0';
9223431Scarlsonj 						(void) snprintf(get_line(0, 0),
9233431Scarlsonj 						    get_line_remain(),
9243431Scarlsonj 						    "  Name = %s", sp);
9253431Scarlsonj 					} while ((sp = sp2) != NULL);
9263431Scarlsonj 					free(str);
9273431Scarlsonj 				}
9283431Scarlsonj 				free(symp);
9293431Scarlsonj 			}
9303431Scarlsonj 			break;
9313431Scarlsonj 		}
9323431Scarlsonj 		case DHCPV6_OPT_SIP_ADDR:
9333431Scarlsonj 		case DHCPV6_OPT_DNS_ADDR:
9343431Scarlsonj 		case DHCPV6_OPT_NIS_SERVERS:
9353431Scarlsonj 		case DHCPV6_OPT_SNTP_SERVERS:
9363431Scarlsonj 		case DHCPV6_OPT_BCMCS_SRV_A:
9373431Scarlsonj 			while (olen >= sizeof (in6_addr_t)) {
9383431Scarlsonj 				show_address("  Address", data);
9393431Scarlsonj 				data += sizeof (in6_addr_t);
9403431Scarlsonj 				olen -= sizeof (in6_addr_t);
9413431Scarlsonj 			}
9423431Scarlsonj 			break;
9433431Scarlsonj 		case DHCPV6_OPT_IAPREFIX: {
9443431Scarlsonj 			dhcpv6_iaprefix_t d6ip;
9453431Scarlsonj 
9463431Scarlsonj 			if (olen < DHCPV6_IAPREFIX_SIZE - sizeof (d6o))
9473431Scarlsonj 				break;
9483431Scarlsonj 			(void) memcpy(&d6ip, data - sizeof (d6o),
9493431Scarlsonj 			    DHCPV6_IAPREFIX_SIZE);
9503431Scarlsonj 			data += DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
9513431Scarlsonj 			olen -= DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
9523431Scarlsonj 			show_address("  Prefix", d6ip.d6ip_addr);
9533431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9543431Scarlsonj 			    "  Preferred lifetime = %u seconds",
9553431Scarlsonj 			    ntohl(d6ip.d6ip_preflife));
9563431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9573431Scarlsonj 			    "  Valid lifetime = %u seconds",
9583431Scarlsonj 			    ntohl(d6ip.d6ip_vallife));
9593431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9603431Scarlsonj 			    "  Prefix length = %u", d6ip.d6ip_preflen);
9613431Scarlsonj 			nest_options(data, olen, "ADDR: ", "Address");
9623431Scarlsonj 			break;
9633431Scarlsonj 		}
9643431Scarlsonj 		case DHCPV6_OPT_INFO_REFTIME:
9653431Scarlsonj 			if (olen < sizeof (val32))
9663431Scarlsonj 				break;
9673431Scarlsonj 			(void) memcpy(&val32, data, sizeof (val32));
9683431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9693431Scarlsonj 			    "  Refresh Time = %lu seconds", ntohl(val32));
9703431Scarlsonj 			break;
9713431Scarlsonj 		case DHCPV6_OPT_GEOCONF_CVC: {
9723431Scarlsonj 			dhcpv6_civic_t d6c;
9733431Scarlsonj 			int solen;
9743431Scarlsonj 
9753431Scarlsonj 			if (olen < DHCPV6_CIVIC_SIZE - sizeof (d6o))
9763431Scarlsonj 				break;
9773431Scarlsonj 			(void) memcpy(&d6c, data - sizeof (d6o),
9783431Scarlsonj 			    DHCPV6_CIVIC_SIZE);
9793431Scarlsonj 			data += DHCPV6_CIVIC_SIZE - sizeof (d6o);
9803431Scarlsonj 			olen -= DHCPV6_CIVIC_SIZE - sizeof (d6o);
9813431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9823431Scarlsonj 			    "  What Location = %u (%s)", d6c.d6c_what,
9833431Scarlsonj 			    cwhat_to_str(d6c.d6c_what));
9843431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
9853431Scarlsonj 			    "  Country Code = %.*s", sizeof (d6c.d6c_cc),
9863431Scarlsonj 			    d6c.d6c_cc);
9873431Scarlsonj 			while (olen >= 2) {
9883431Scarlsonj 				(void) snprintf(get_line(0, 0),
9893431Scarlsonj 				    get_line_remain(),
9903431Scarlsonj 				    "  CA Element = %u (%s)", *data,
9913431Scarlsonj 				    catype_to_str(*data));
9923431Scarlsonj 				solen = data[1];
9933431Scarlsonj 				data += 2;
9943431Scarlsonj 				olen -= 2;
9953431Scarlsonj 				if (solen > olen) {
9963431Scarlsonj 					(void) strlcpy(get_line(0, 0),
9973431Scarlsonj 					    "  CA Element truncated",
9983431Scarlsonj 					    get_line_remain());
9993431Scarlsonj 					solen = olen;
10003431Scarlsonj 				}
10013431Scarlsonj 				if (solen > 0) {
10023431Scarlsonj 					show_ascii(data, solen, "  CA Data");
10033431Scarlsonj 					data += solen;
10043431Scarlsonj 					olen -= solen;
10053431Scarlsonj 				}
10063431Scarlsonj 			}
10073431Scarlsonj 			break;
10083431Scarlsonj 		}
10093431Scarlsonj 		case DHCPV6_OPT_CLIENT_FQDN: {
10103431Scarlsonj 			dhcp_symbol_t *symp;
10113431Scarlsonj 
10123431Scarlsonj 			if (olen == 0)
10133431Scarlsonj 				break;
10143431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
10153431Scarlsonj 			    "  Flags = %02x", *data);
10163431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
10173431Scarlsonj 			    "        %s", getflag(*data, DHCPV6_FQDNF_S,
10183431Scarlsonj 			    "Perform AAAA RR updates", "No AAAA RR updates"));
10193431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
10203431Scarlsonj 			    "        %s", getflag(*data, DHCPV6_FQDNF_O,
10213431Scarlsonj 			    "Server override updates",
10223431Scarlsonj 			    "No server override updates"));
10233431Scarlsonj 			(void) snprintf(get_line(0, 0), get_line_remain(),
10243431Scarlsonj 			    "        %s", getflag(*data, DHCPV6_FQDNF_N,
10253431Scarlsonj 			    "Server performs no updates",
10263431Scarlsonj 			    "Server performs updates"));
10273431Scarlsonj 			symp = inittab_getbycode(
10283431Scarlsonj 			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
10293431Scarlsonj 			    d6o.d6o_code);
10303431Scarlsonj 			if (symp != NULL) {
10313431Scarlsonj 				str = inittab_decode(symp, data, olen, B_TRUE);
10323431Scarlsonj 				if (str != NULL) {
10333431Scarlsonj 					(void) snprintf(get_line(0, 0),
10343431Scarlsonj 					    get_line_remain(),
10353431Scarlsonj 					    "  FQDN = %s", str);
10363431Scarlsonj 					free(str);
10373431Scarlsonj 				}
10383431Scarlsonj 				free(symp);
10393431Scarlsonj 			}
10403431Scarlsonj 			break;
10413431Scarlsonj 		}
10423431Scarlsonj 		}
10433431Scarlsonj 		data = ostart + d6o.d6o_len;
10443431Scarlsonj 		len -= d6o.d6o_len;
10453431Scarlsonj 	}
10463431Scarlsonj 	if (len != 0) {
10473431Scarlsonj 		(void) strlcpy(get_line(0, 0), "Option entry truncated",
10483431Scarlsonj 		    get_line_remain());
10493431Scarlsonj 	}
10503431Scarlsonj }
1051