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