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