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