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