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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <string.h>
  28 #include <fcntl.h>
  29 #include <string.h>
  30 #include <sys/types.h>
  31 #include <sys/time.h>
  32 
  33 #include <sys/stropts.h>
  34 #include <sys/socket.h>
  35 #include <net/if.h>
  36 #include <netinet/in_systm.h>
  37 #include <netinet/in.h>
  38 #include <netinet/ip.h>
  39 #include <netinet/ip6.h>
  40 #include <netinet/ip_icmp.h>
  41 #include <netinet/icmp6.h>
  42 #include <netinet/if_ether.h>
  43 #include <inet/ip.h>
  44 #include <inet/ip6.h>
  45 #include <arpa/inet.h>
  46 #include <netdb.h>
  47 #include <tsol/label.h>
  48 #include <sys/tsol/tndb.h>
  49 #include <sys/tsol/label_macro.h>
  50 
  51 #include "snoop.h"
  52 
  53 
  54 /*
  55  * IPv6 extension header masks.  These are used by the print_ipv6_extensions()
  56  * function to return information to the caller about which extension headers
  57  * were processed.  This can be useful if the caller wants to know if the
  58  * packet is an IPv6 fragment, for example.
  59  */
  60 #define SNOOP_HOPOPTS   0x01U
  61 #define SNOOP_ROUTING   0x02U
  62 #define SNOOP_DSTOPTS   0x04U
  63 #define SNOOP_FRAGMENT  0x08U
  64 #define SNOOP_AH        0x10U
  65 #define SNOOP_ESP       0x20U
  66 #define SNOOP_IPV6      0x40U
  67 
  68 static void prt_routing_hdr(int, const struct ip6_rthdr *);
  69 static void prt_fragment_hdr(int, const struct ip6_frag *);
  70 static void prt_hbh_options(int, const struct ip6_hbh *);
  71 static void prt_dest_options(int, const struct ip6_dest *);
  72 static void print_route(const uchar_t *);
  73 static void print_ipoptions(const uchar_t *, int);
  74 static void print_ripso(const uchar_t *);
  75 static void print_cipso(const uchar_t *);
  76 
  77 /* Keep track of how many nested IP headers we have. */
  78 unsigned int encap_levels;
  79 unsigned int total_encap_levels = 1;
  80 
  81 int
  82 interpret_ip(int flags, const struct ip *ip, int fraglen)
  83 {
  84         uchar_t *data;
  85         char buff[24];
  86         boolean_t isfrag = B_FALSE;
  87         boolean_t morefrag;
  88         uint16_t fragoffset;
  89         int hdrlen;
  90         uint16_t iplen, uitmp;
  91 
  92         if (ip->ip_v == IPV6_VERSION) {
  93                 iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen);
  94                 return (iplen);
  95         }
  96 
  97         if (encap_levels == 0)
  98                 total_encap_levels = 0;
  99         encap_levels++;
 100         total_encap_levels++;
 101 
 102         hdrlen = ip->ip_hl * 4;
 103         data = ((uchar_t *)ip) + hdrlen;
 104         iplen = ntohs(ip->ip_len) - hdrlen;
 105         fraglen -= hdrlen;
 106         if (fraglen > iplen)
 107                 fraglen = iplen;
 108         if (fraglen < 0) {
 109                 (void) snprintf(get_sum_line(), MAXLINE,
 110                     "IP truncated: header missing %d bytes", -fraglen);
 111                 encap_levels--;
 112                 return (fraglen + iplen);
 113         }
 114         /*
 115          * We flag this as a fragment if the more fragments bit is set, or
 116          * if the fragment offset is non-zero.
 117          */
 118         morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE;
 119         fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8;
 120         if (morefrag || fragoffset != 0)
 121                 isfrag = B_TRUE;
 122 
 123         src_name = addrtoname(AF_INET, &ip->ip_src);
 124         dst_name = addrtoname(AF_INET, &ip->ip_dst);
 125 
 126         if (flags & F_SUM) {
 127                 if (isfrag) {
 128                         (void) snprintf(get_sum_line(), MAXLINE,
 129                             "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x "
 130                             "TTL=%d",
 131                             getproto(ip->ip_p),
 132                             ntohs(ip->ip_id),
 133                             fragoffset,
 134                             morefrag,
 135                             ip->ip_tos,
 136                             ip->ip_ttl);
 137                 } else {
 138                         (void) strlcpy(buff, inet_ntoa(ip->ip_dst),
 139                             sizeof (buff));
 140                         uitmp = ntohs(ip->ip_len);
 141                         (void) snprintf(get_sum_line(), MAXLINE,
 142                             "IP  D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d",
 143                             buff,
 144                             inet_ntoa(ip->ip_src),
 145                             uitmp,
 146                             iplen > fraglen ? "?" : "",
 147                             ntohs(ip->ip_id),
 148                             ip->ip_tos,
 149                             ip->ip_ttl);
 150                 }
 151         }
 152 
 153         if (flags & F_DTAIL) {
 154                 show_header("IP:   ", "IP Header", iplen);
 155                 show_space();
 156                 (void) snprintf(get_line(0, 0), get_line_remain(),
 157                     "Version = %d", ip->ip_v);
 158                 (void) snprintf(get_line(0, 0), get_line_remain(),
 159                     "Header length = %d bytes", hdrlen);
 160                 (void) snprintf(get_line(0, 0), get_line_remain(),
 161                     "Type of service = 0x%02x", ip->ip_tos);
 162                 (void) snprintf(get_line(0, 0), get_line_remain(),
 163                     "      xxx. .... = %d (precedence)",
 164                     ip->ip_tos >> 5);
 165                 (void) snprintf(get_line(0, 0), get_line_remain(),
 166                     "      %s", getflag(ip->ip_tos, IPTOS_LOWDELAY,
 167                     "low delay", "normal delay"));
 168                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 169                     getflag(ip->ip_tos, IPTOS_THROUGHPUT,
 170                     "high throughput", "normal throughput"));
 171                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 172                     getflag(ip->ip_tos, IPTOS_RELIABILITY,
 173                     "high reliability", "normal reliability"));
 174                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 175                     getflag(ip->ip_tos, IPTOS_ECT,
 176                     "ECN capable transport", "not ECN capable transport"));
 177                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 178                     getflag(ip->ip_tos, IPTOS_CE,
 179                     "ECN congestion experienced",
 180                     "no ECN congestion experienced"));
 181                 /* warning: ip_len is signed in netinet/ip.h */
 182                 uitmp = ntohs(ip->ip_len);
 183                 (void) snprintf(get_line(0, 0), get_line_remain(),
 184                     "Total length = %u bytes%s", uitmp,
 185                     iplen > fraglen ? " -- truncated" : "");
 186                 (void) snprintf(get_line(0, 0), get_line_remain(),
 187                     "Identification = %d", ntohs(ip->ip_id));
 188                 /* warning: ip_off is signed in netinet/ip.h */
 189                 uitmp = ntohs(ip->ip_off);
 190                 (void) snprintf(get_line(0, 0), get_line_remain(),
 191                     "Flags = 0x%x", uitmp >> 12);
 192                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 193                     getflag(uitmp >> 8, IP_DF >> 8,
 194                     "do not fragment", "may fragment"));
 195                 (void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
 196                     getflag(uitmp >> 8, IP_MF >> 8,
 197                     "more fragments", "last fragment"));
 198                 (void) snprintf(get_line(0, 0), get_line_remain(),
 199                     "Fragment offset = %u bytes",
 200                     fragoffset);
 201                 (void) snprintf(get_line(0, 0), get_line_remain(),
 202                     "Time to live = %d seconds/hops",
 203                     ip->ip_ttl);
 204                 (void) snprintf(get_line(0, 0), get_line_remain(),
 205                     "Protocol = %d (%s)", ip->ip_p,
 206                     getproto(ip->ip_p));
 207                 /*
 208                  * XXX need to compute checksum and print whether it's correct
 209                  */
 210                 (void) snprintf(get_line(0, 0), get_line_remain(),
 211                     "Header checksum = %04x",
 212                     ntohs(ip->ip_sum));
 213                 (void) snprintf(get_line(0, 0), get_line_remain(),
 214                     "Source address = %s, %s",
 215                     inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src));
 216                 (void) snprintf(get_line(0, 0), get_line_remain(),
 217                     "Destination address = %s, %s",
 218                     inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst));
 219 
 220                 /* Print IP options - if any */
 221 
 222                 print_ipoptions((const uchar_t *)(ip + 1),
 223                     hdrlen - sizeof (struct ip));
 224                 show_space();
 225         }
 226 
 227         /*
 228          * If we are in detail mode, and this is not the first fragment of
 229          * a fragmented packet, print out a little line stating this.
 230          * Otherwise, go to the next protocol layer only if this is not a
 231          * fragment, or we are in detail mode and this is the first fragment
 232          * of a fragmented packet.
 233          */
 234         if (flags & F_DTAIL && fragoffset != 0) {
 235                 (void) snprintf(get_detail_line(0, 0), MAXLINE,
 236                     "%s:  [%d byte(s) of data, continuation of IP ident=%d]",
 237                     getproto(ip->ip_p),
 238                     iplen,
 239                     ntohs(ip->ip_id));
 240         } else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) {
 241                 /* go to the next protocol layer */
 242 
 243                 if (fraglen > 0) {
 244                         switch (ip->ip_p) {
 245                         case IPPROTO_IP:
 246                                 break;
 247                         case IPPROTO_ENCAP:
 248                                 (void) interpret_ip(flags,
 249                                     /* LINTED: alignment */
 250                                     (const struct ip *)data, fraglen);
 251                                 break;
 252                         case IPPROTO_ICMP:
 253                                 (void) interpret_icmp(flags,
 254                                     /* LINTED: alignment */
 255                                     (struct icmp *)data, iplen, fraglen);
 256                                 break;
 257                         case IPPROTO_IGMP:
 258                                 interpret_igmp(flags, data, iplen, fraglen);
 259                                 break;
 260                         case IPPROTO_GGP:
 261                                 break;
 262                         case IPPROTO_TCP:
 263                                 (void) interpret_tcp(flags,
 264                                     (struct tcphdr *)data, iplen, fraglen);
 265                                 break;
 266 
 267                         case IPPROTO_ESP:
 268                                 (void) interpret_esp(flags, data, iplen,
 269                                     fraglen);
 270                                 break;
 271                         case IPPROTO_AH:
 272                                 (void) interpret_ah(flags, data, iplen,
 273                                     fraglen);
 274                                 break;
 275 
 276                         case IPPROTO_OSPF:
 277                                 interpret_ospf(flags, data, iplen, fraglen);
 278                                 break;
 279 
 280                         case IPPROTO_EGP:
 281                         case IPPROTO_PUP:
 282                                 break;
 283                         case IPPROTO_UDP:
 284                                 (void) interpret_udp(flags,
 285                                     (struct udphdr *)data, iplen, fraglen);
 286                                 break;
 287 
 288                         case IPPROTO_IDP:
 289                         case IPPROTO_HELLO:
 290                         case IPPROTO_ND:
 291                         case IPPROTO_RAW:
 292                                 break;
 293                         case IPPROTO_IPV6:      /* IPV6 encap */
 294                                 /* LINTED: alignment */
 295                                 (void) interpret_ipv6(flags, (ip6_t *)data,
 296                                     iplen);
 297                                 break;
 298                         case IPPROTO_SCTP:
 299                                 (void) interpret_sctp(flags,
 300                                     (struct sctp_hdr *)data, iplen, fraglen);
 301                                 break;
 302                         case IPPROTO_DCCP:
 303                                 (void) interpret_dccp(flags,
 304                                     (struct dccphdr *)data, iplen, fraglen);
 305                                 break;
 306                         }
 307                 }
 308         }
 309 
 310         encap_levels--;
 311         return (iplen);
 312 }
 313 
 314 int
 315 interpret_ipv6(int flags, const ip6_t *ip6h, int fraglen)
 316 {
 317         uint8_t *data;
 318         int hdrlen, iplen;
 319         int version, flow, class;
 320         uchar_t proto;
 321         boolean_t isfrag = B_FALSE;
 322         uint8_t extmask;
 323         /*
 324          * The print_srcname and print_dstname strings are the hostname
 325          * parts of the verbose IPv6 header output, including the comma
 326          * and the space after the litteral address strings.
 327          */
 328         char print_srcname[MAXHOSTNAMELEN + 2];
 329         char print_dstname[MAXHOSTNAMELEN + 2];
 330         char src_addrstr[INET6_ADDRSTRLEN];
 331         char dst_addrstr[INET6_ADDRSTRLEN];
 332 
 333         iplen = ntohs(ip6h->ip6_plen);
 334         hdrlen = IPV6_HDR_LEN;
 335         fraglen -= hdrlen;
 336         if (fraglen < 0)
 337                 return (fraglen + hdrlen);
 338         data = ((uint8_t *)ip6h) + hdrlen;
 339 
 340         proto = ip6h->ip6_nxt;
 341 
 342         src_name = addrtoname(AF_INET6, &ip6h->ip6_src);
 343         dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst);
 344 
 345         /*
 346          * Use endian-aware masks to extract traffic class and
 347          * flowinfo.  Also, flowinfo is now 20 bits and class 8
 348          * rather than 24 and 4.
 349          */
 350         class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20);
 351         flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL);
 352 
 353         /*
 354          * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive,
 355          * so the code within the first part of the following if statement
 356          * will not affect the detailed printing of the packet.
 357          */
 358         if (flags & F_SUM) {
 359                 (void) snprintf(get_sum_line(), MAXLINE,
 360                     "IPv6  S=%s D=%s LEN=%d HOPS=%d CLASS=0x%x FLOW=0x%x",
 361                     src_name, dst_name, iplen, ip6h->ip6_hops, class, flow);
 362         } else if (flags & F_DTAIL) {
 363 
 364                 (void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr,
 365                     INET6_ADDRSTRLEN);
 366                 (void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr,
 367                     INET6_ADDRSTRLEN);
 368 
 369                 version = ntohl(ip6h->ip6_vcf) >> 28;
 370 
 371                 if (strcmp(src_name, src_addrstr) == 0) {
 372                         print_srcname[0] = '\0';
 373                 } else {
 374                         snprintf(print_srcname, sizeof (print_srcname),
 375                             ", %s", src_name);
 376                 }
 377 
 378                 if (strcmp(dst_name, dst_addrstr) == 0) {
 379                         print_dstname[0] = '\0';
 380                 } else {
 381                         snprintf(print_dstname, sizeof (print_dstname),
 382                             ", %s", dst_name);
 383                 }
 384 
 385                 show_header("IPv6:   ", "IPv6 Header", iplen);
 386                 show_space();
 387 
 388                 (void) snprintf(get_line(0, 0), get_line_remain(),
 389                     "Version = %d", version);
 390                 (void) snprintf(get_line(0, 0), get_line_remain(),
 391                     "Traffic Class = %d", class);
 392                 (void) snprintf(get_line(0, 0), get_line_remain(),
 393                     "Flow label = 0x%x", flow);
 394                 (void) snprintf(get_line(0, 0), get_line_remain(),
 395                     "Payload length = %d", iplen);
 396                 (void) snprintf(get_line(0, 0), get_line_remain(),
 397                     "Next Header = %d (%s)", proto,
 398                     getproto(proto));
 399                 (void) snprintf(get_line(0, 0), get_line_remain(),
 400                     "Hop Limit = %d", ip6h->ip6_hops);
 401                 (void) snprintf(get_line(0, 0), get_line_remain(),
 402                     "Source address = %s%s", src_addrstr, print_srcname);
 403                 (void) snprintf(get_line(0, 0), get_line_remain(),
 404                     "Destination address = %s%s", dst_addrstr, print_dstname);
 405 
 406                 show_space();
 407         }
 408 
 409         /*
 410          * Print IPv6 Extension Headers, or skip them in the summary case.
 411          * Set isfrag to true if one of the extension headers encounterred
 412          * was a fragment header.
 413          */
 414         if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
 415             proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
 416                 extmask = print_ipv6_extensions(flags, &data, &proto, &iplen,
 417                     &fraglen);
 418                 if ((extmask & SNOOP_FRAGMENT) != 0) {
 419                         isfrag = B_TRUE;
 420                 }
 421         }
 422 
 423         /*
 424          * We only want to print upper layer information if this is not
 425          * a fragment, or if we're printing in detail.  Note that the
 426          * proto variable will be set to IPPROTO_NONE if this is a fragment
 427          * with a non-zero fragment offset.
 428          */
 429         if (!isfrag || flags & F_DTAIL) {
 430                 /* go to the next protocol layer */
 431 
 432                 switch (proto) {
 433                 case IPPROTO_IP:
 434                         break;
 435                 case IPPROTO_ENCAP:
 436                         /* LINTED: alignment */
 437                         (void) interpret_ip(flags, (const struct ip *)data,
 438                             fraglen);
 439                         break;
 440                 case IPPROTO_ICMPV6:
 441                         /* LINTED: alignment */
 442                         (void) interpret_icmpv6(flags, (icmp6_t *)data, iplen,
 443                             fraglen);
 444                         break;
 445                 case IPPROTO_IGMP:
 446                         interpret_igmp(flags, data, iplen, fraglen);
 447                         break;
 448                 case IPPROTO_GGP:
 449                         break;
 450                 case IPPROTO_TCP:
 451                         (void) interpret_tcp(flags, (struct tcphdr *)data,
 452                             iplen, fraglen);
 453                         break;
 454                 case IPPROTO_ESP:
 455                         (void) interpret_esp(flags, data, iplen, fraglen);
 456                         break;
 457                 case IPPROTO_AH:
 458                         (void) interpret_ah(flags, data, iplen, fraglen);
 459                         break;
 460                 case IPPROTO_EGP:
 461                 case IPPROTO_PUP:
 462                         break;
 463                 case IPPROTO_UDP:
 464                         (void) interpret_udp(flags, (struct udphdr *)data,
 465                             iplen, fraglen);
 466                         break;
 467                 case IPPROTO_IDP:
 468                 case IPPROTO_HELLO:
 469                 case IPPROTO_ND:
 470                 case IPPROTO_RAW:
 471                         break;
 472                 case IPPROTO_IPV6:
 473                         /* LINTED: alignment */
 474                         (void) interpret_ipv6(flags, (const ip6_t *)data,
 475                             iplen);
 476                         break;
 477                 case IPPROTO_SCTP:
 478                         (void) interpret_sctp(flags, (struct sctp_hdr *)data,
 479                             iplen, fraglen);
 480                         break;
 481                 case IPPROTO_OSPF:
 482                         interpret_ospf6(flags, data, iplen, fraglen);
 483                         break;
 484                 case IPPROTO_DCCP:
 485                         (void) interpret_dccp(flags, (struct dccphdr *)data,
 486                             iplen, fraglen);
 487                         break;
 488                 }
 489         }
 490 
 491         return (iplen);
 492 }
 493 
 494 /*
 495  * ip_ext: data including the extension header.
 496  * iplen: length of the data remaining in the packet.
 497  * Returns a mask of IPv6 extension headers it processed.
 498  */
 499 uint8_t
 500 print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen,
 501     int *fraglen)
 502 {
 503         uint8_t *data_ptr;
 504         uchar_t proto = *next;
 505         boolean_t is_extension_header;
 506         struct ip6_hbh *ipv6ext_hbh;
 507         struct ip6_dest *ipv6ext_dest;
 508         struct ip6_rthdr *ipv6ext_rthdr;
 509         struct ip6_frag *ipv6ext_frag;
 510         uint32_t exthdrlen;
 511         uint8_t extmask = 0;
 512 
 513         if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0))
 514                 return (0);
 515 
 516         data_ptr = *hdr;
 517         is_extension_header = B_TRUE;
 518         while (is_extension_header) {
 519 
 520                 /*
 521                  * There must be at least enough data left to read the
 522                  * next header and header length fields from the next
 523                  * header.
 524                  */
 525                 if (*fraglen < 2) {
 526                         return (extmask);
 527                 }
 528 
 529                 switch (proto) {
 530                 case IPPROTO_HOPOPTS:
 531                         ipv6ext_hbh = (struct ip6_hbh *)data_ptr;
 532                         exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8;
 533                         if (*fraglen <= exthdrlen) {
 534                                 return (extmask);
 535                         }
 536                         prt_hbh_options(flags, ipv6ext_hbh);
 537                         extmask |= SNOOP_HOPOPTS;
 538                         proto = ipv6ext_hbh->ip6h_nxt;
 539                         break;
 540                 case IPPROTO_DSTOPTS:
 541                         ipv6ext_dest = (struct ip6_dest *)data_ptr;
 542                         exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8;
 543                         if (*fraglen <= exthdrlen) {
 544                                 return (extmask);
 545                         }
 546                         prt_dest_options(flags, ipv6ext_dest);
 547                         extmask |= SNOOP_DSTOPTS;
 548                         proto = ipv6ext_dest->ip6d_nxt;
 549                         break;
 550                 case IPPROTO_ROUTING:
 551                         ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr;
 552                         exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8;
 553                         if (*fraglen <= exthdrlen) {
 554                                 return (extmask);
 555                         }
 556                         prt_routing_hdr(flags, ipv6ext_rthdr);
 557                         extmask |= SNOOP_ROUTING;
 558                         proto = ipv6ext_rthdr->ip6r_nxt;
 559                         break;
 560                 case IPPROTO_FRAGMENT:
 561                         /* LINTED: alignment */
 562                         ipv6ext_frag = (struct ip6_frag *)data_ptr;
 563                         exthdrlen = sizeof (struct ip6_frag);
 564                         if (*fraglen <= exthdrlen) {
 565                                 return (extmask);
 566                         }
 567                         prt_fragment_hdr(flags, ipv6ext_frag);
 568                         extmask |= SNOOP_FRAGMENT;
 569                         /*
 570                          * If this is not the first fragment, forget about
 571                          * the rest of the packet, snoop decoding is
 572                          * stateless.
 573                          */
 574                         if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0)
 575                                 proto = IPPROTO_NONE;
 576                         else
 577                                 proto = ipv6ext_frag->ip6f_nxt;
 578                         break;
 579                 default:
 580                         is_extension_header = B_FALSE;
 581                         break;
 582                 }
 583 
 584                 if (is_extension_header) {
 585                         *iplen -= exthdrlen;
 586                         *fraglen -= exthdrlen;
 587                         data_ptr += exthdrlen;
 588                 }
 589         }
 590 
 591         *next = proto;
 592         *hdr = data_ptr;
 593         return (extmask);
 594 }
 595 
 596 static void
 597 print_ipoptions(const uchar_t *opt, int optlen)
 598 {
 599         int len;
 600         int remain;
 601         char *line;
 602         const char *truncstr;
 603 
 604         if (optlen <= 0) {
 605                 (void) snprintf(get_line(0, 0), get_line_remain(),
 606                     "No options");
 607                 return;
 608         }
 609 
 610         (void) snprintf(get_line(0, 0), get_line_remain(),
 611             "Options: (%d bytes)", optlen);
 612 
 613         while (optlen > 0) {
 614                 line = get_line(0, 0);
 615                 remain = get_line_remain();
 616                 len = opt[1];
 617                 truncstr = len > optlen ? "?" : "";
 618                 switch (opt[0]) {
 619                 case IPOPT_EOL:
 620                         (void) strlcpy(line, "  - End of option list", remain);
 621                         return;
 622                 case IPOPT_NOP:
 623                         (void) strlcpy(line, "  - No op", remain);
 624                         len = 1;
 625                         break;
 626                 case IPOPT_RR:
 627                         (void) snprintf(line, remain,
 628                             "  - Record route (%d bytes%s)", len, truncstr);
 629                         print_route(opt);
 630                         break;
 631                 case IPOPT_TS:
 632                         (void) snprintf(line, remain,
 633                             "  - Time stamp (%d bytes%s)", len, truncstr);
 634                         break;
 635                 case IPOPT_SECURITY:
 636                         (void) snprintf(line, remain, "  - RIPSO (%d bytes%s)",
 637                             len, truncstr);
 638                         print_ripso(opt);
 639                         break;
 640                 case IPOPT_COMSEC:
 641                         (void) snprintf(line, remain, "  - CIPSO (%d bytes%s)",
 642                             len, truncstr);
 643                         print_cipso(opt);
 644                         break;
 645                 case IPOPT_LSRR:
 646                         (void) snprintf(line, remain,
 647                             "  - Loose source route (%d bytes%s)", len,
 648                             truncstr);
 649                         print_route(opt);
 650                         break;
 651                 case IPOPT_SATID:
 652                         (void) snprintf(line, remain,
 653                             "  - SATNET Stream id (%d bytes%s)",
 654                             len, truncstr);
 655                         break;
 656                 case IPOPT_SSRR:
 657                         (void) snprintf(line, remain,
 658                             "  - Strict source route, (%d bytes%s)", len,
 659                             truncstr);
 660                         print_route(opt);
 661                         break;
 662                 default:
 663                         (void) snprintf(line, remain,
 664                             "  - Option %d (unknown - %d bytes%s) %s",
 665                             opt[0], len, truncstr,
 666                             tohex((char *)&opt[2], len - 2));
 667                         break;
 668                 }
 669                 if (len <= 0) {
 670                         (void) snprintf(line, remain,
 671                             "  - Incomplete option len %d", len);
 672                         break;
 673                 }
 674                 opt += len;
 675                 optlen -= len;
 676         }
 677 }
 678 
 679 static void
 680 print_route(const uchar_t *opt)
 681 {
 682         int len, pointer, remain;
 683         struct in_addr addr;
 684         char *line;
 685 
 686         len = opt[1];
 687         pointer = opt[2];
 688 
 689         (void) snprintf(get_line(0, 0), get_line_remain(),
 690             "    Pointer = %d", pointer);
 691 
 692         pointer -= IPOPT_MINOFF;
 693         opt += (IPOPT_OFFSET + 1);
 694         len -= (IPOPT_OFFSET + 1);
 695 
 696         while (len > 0) {
 697                 line = get_line(0, 0);
 698                 remain = get_line_remain();
 699                 memcpy((char *)&addr, opt, sizeof (addr));
 700                 if (addr.s_addr == INADDR_ANY)
 701                         (void) strlcpy(line, "      -", remain);
 702                 else
 703                         (void) snprintf(line, remain, "      %s",
 704                             addrtoname(AF_INET, &addr));
 705                 if (pointer == 0)
 706                         (void) strlcat(line, "  <-- (current)", remain);
 707 
 708                 opt += sizeof (addr);
 709                 len -= sizeof (addr);
 710                 pointer -= sizeof (addr);
 711         }
 712 }
 713 
 714 char *
 715 getproto(int p)
 716 {
 717         switch (p) {
 718         case IPPROTO_HOPOPTS:   return ("IPv6-HopOpts");
 719         case IPPROTO_IPV6:      return ("IPv6");
 720         case IPPROTO_ROUTING:   return ("IPv6-Route");
 721         case IPPROTO_FRAGMENT:  return ("IPv6-Frag");
 722         case IPPROTO_RSVP:      return ("RSVP");
 723         case IPPROTO_ENCAP:     return ("IP-in-IP");
 724         case IPPROTO_AH:        return ("AH");
 725         case IPPROTO_ESP:       return ("ESP");
 726         case IPPROTO_ICMP:      return ("ICMP");
 727         case IPPROTO_ICMPV6:    return ("ICMPv6");
 728         case IPPROTO_DSTOPTS:   return ("IPv6-DstOpts");
 729         case IPPROTO_IGMP:      return ("IGMP");
 730         case IPPROTO_GGP:       return ("GGP");
 731         case IPPROTO_TCP:       return ("TCP");
 732         case IPPROTO_EGP:       return ("EGP");
 733         case IPPROTO_PUP:       return ("PUP");
 734         case IPPROTO_UDP:       return ("UDP");
 735         case IPPROTO_IDP:       return ("IDP");
 736         case IPPROTO_HELLO:     return ("HELLO");
 737         case IPPROTO_ND:        return ("ND");
 738         case IPPROTO_EON:       return ("EON");
 739         case IPPROTO_RAW:       return ("RAW");
 740         case IPPROTO_OSPF:      return ("OSPF");
 741         case IPPROTO_DCCP:      return ("DCCP");
 742         default:                return ("");
 743         }
 744 }
 745 
 746 static void
 747 prt_routing_hdr(int flags, const struct ip6_rthdr *ipv6ext_rthdr)
 748 {
 749         uint8_t nxt_hdr;
 750         uint8_t type;
 751         uint32_t len;
 752         uint8_t segleft;
 753         uint32_t numaddrs;
 754         int i;
 755         struct ip6_rthdr0 *ipv6ext_rthdr0;
 756         struct in6_addr *addrs;
 757         char addr[INET6_ADDRSTRLEN];
 758 
 759         /* in summary mode, we don't do anything. */
 760         if (flags & F_SUM) {
 761                 return;
 762         }
 763 
 764         nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
 765         type = ipv6ext_rthdr->ip6r_type;
 766         len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
 767         segleft = ipv6ext_rthdr->ip6r_segleft;
 768 
 769         show_header("IPv6-Route:  ", "IPv6 Routing Header", 0);
 770         show_space();
 771 
 772         (void) snprintf(get_line(0, 0), get_line_remain(),
 773             "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
 774         (void) snprintf(get_line(0, 0), get_line_remain(),
 775             "Header length = %d", len);
 776         (void) snprintf(get_line(0, 0), get_line_remain(),
 777             "Routing type = %d", type);
 778         (void) snprintf(get_line(0, 0), get_line_remain(),
 779             "Segments left = %d", segleft);
 780 
 781         if (type == IPV6_RTHDR_TYPE_0) {
 782                 /*
 783                  * XXX This loop will print all addresses in the routing header,
 784                  * XXX not just the segments left.
 785                  * XXX (The header length field is twice the number of
 786                  * XXX addresses)
 787                  * XXX At some future time, we may want to change this
 788                  * XXX to differentiate between the hops yet to do
 789                  * XXX and the hops already taken.
 790                  */
 791                 /* LINTED: alignment */
 792                 ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
 793                 numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
 794                 addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
 795                 for (i = 0; i < numaddrs; i++) {
 796                         (void) inet_ntop(AF_INET6, &addrs[i], addr,
 797                             INET6_ADDRSTRLEN);
 798                         (void) snprintf(get_line(0, 0), get_line_remain(),
 799                             "address[%d]=%s", i, addr);
 800                 }
 801         }
 802 
 803         show_space();
 804 }
 805 
 806 static void
 807 prt_fragment_hdr(int flags, const struct ip6_frag *ipv6ext_frag)
 808 {
 809         boolean_t morefrag;
 810         uint16_t fragoffset;
 811         uint8_t nxt_hdr;
 812         uint32_t fragident;
 813 
 814         /* extract the various fields from the fragment header */
 815         nxt_hdr = ipv6ext_frag->ip6f_nxt;
 816         morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
 817             ? B_FALSE : B_TRUE;
 818         fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
 819         fragident = ntohl(ipv6ext_frag->ip6f_ident);
 820 
 821         if (flags & F_SUM) {
 822                 (void) snprintf(get_sum_line(), MAXLINE,
 823                     "IPv6 fragment ID=%u Offset=%-4d MF=%d",
 824                     fragident,
 825                     fragoffset,
 826                     morefrag);
 827         } else { /* F_DTAIL */
 828                 show_header("IPv6-Frag:  ", "IPv6 Fragment Header", 0);
 829                 show_space();
 830 
 831                 (void) snprintf(get_line(0, 0), get_line_remain(),
 832                     "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
 833                 (void) snprintf(get_line(0, 0), get_line_remain(),
 834                     "Fragment Offset = %d", fragoffset);
 835                 (void) snprintf(get_line(0, 0), get_line_remain(),
 836                     "More Fragments Flag = %s", morefrag ? "true" : "false");
 837                 (void) snprintf(get_line(0, 0), get_line_remain(),
 838                     "Identification = %u", fragident);
 839 
 840                 show_space();
 841         }
 842 }
 843 
 844 static void
 845 print_ip6opt_ls(const uchar_t *data, unsigned int op_len)
 846 {
 847         uint32_t doi;
 848         uint8_t sotype, solen;
 849         uint16_t value, value2;
 850         char *cp;
 851         int remlen;
 852         boolean_t printed;
 853 
 854         (void) snprintf(get_line(0, 0), get_line_remain(),
 855             "Labeled Security Option len = %u bytes%s", op_len,
 856             op_len < sizeof (uint32_t) || (op_len & 1) != 0 ? "?" : "");
 857         if (op_len < sizeof (uint32_t))
 858                 return;
 859         GETINT32(doi, data);
 860         (void) snprintf(get_line(0, 0), get_line_remain(),
 861             "    DOI = %d (%s)", doi, doi == IP6LS_DOI_V4 ? "IPv4" : "???");
 862         op_len -= sizeof (uint32_t);
 863         while (op_len > 0) {
 864                 GETINT8(sotype, data);
 865                 if (op_len < 2) {
 866                         (void) snprintf(get_line(0, 0), get_line_remain(),
 867                             "    truncated %u suboption (no len)", sotype);
 868                         break;
 869                 }
 870                 GETINT8(solen, data);
 871                 if (solen < 2 || solen > op_len) {
 872                         (void) snprintf(get_line(0, 0), get_line_remain(),
 873                             "    bad %u suboption (len 2 <= %u <= %u)",
 874                             sotype, solen, op_len);
 875                         if (solen < 2)
 876                                 solen = 2;
 877                         if (solen > op_len)
 878                                 solen = op_len;
 879                 }
 880                 op_len -= solen;
 881                 solen -= 2;
 882                 cp = get_line(0, 0);
 883                 remlen = get_line_remain();
 884                 (void) strlcpy(cp, "    ", remlen);
 885                 cp += 4;
 886                 remlen -= 4;
 887                 printed = B_TRUE;
 888                 switch (sotype) {
 889                 case IP6LS_TT_LEVEL:
 890                         if (solen != 2) {
 891                                 printed = B_FALSE;
 892                                 break;
 893                         }
 894                         GETINT16(value, data);
 895                         (void) snprintf(cp, remlen, "Level %u", value);
 896                         solen = 0;
 897                         break;
 898                 case IP6LS_TT_VECTOR:
 899                         (void) strlcpy(cp, "Bit-Vector: ", remlen);
 900                         remlen -= strlen(cp);
 901                         cp += strlen(cp);
 902                         while (solen > 1) {
 903                                 GETINT16(value, data);
 904                                 solen -= 2;
 905                                 (void) snprintf(cp, remlen, "%04x", value);
 906                                 remlen -= strlen(cp);
 907                                 cp += strlen(cp);
 908                         }
 909                         break;
 910                 case IP6LS_TT_ENUM:
 911                         (void) strlcpy(cp, "Enumeration:", remlen);
 912                         remlen -= strlen(cp);
 913                         cp += strlen(cp);
 914                         while (solen > 1) {
 915                                 GETINT16(value, data);
 916                                 solen -= 2;
 917                                 (void) snprintf(cp, remlen, " %u", value);
 918                                 remlen -= strlen(cp);
 919                                 cp += strlen(cp);
 920                         }
 921                         break;
 922                 case IP6LS_TT_RANGES:
 923                         (void) strlcpy(cp, "Ranges:", remlen);
 924                         remlen -= strlen(cp);
 925                         cp += strlen(cp);
 926                         while (solen > 3) {
 927                                 GETINT16(value, data);
 928                                 GETINT16(value2, data);
 929                                 solen -= 4;
 930                                 (void) snprintf(cp, remlen, " %u-%u", value,
 931                                     value2);
 932                                 remlen -= strlen(cp);
 933                                 cp += strlen(cp);
 934                         }
 935                         break;
 936                 case IP6LS_TT_V4:
 937                         (void) strlcpy(cp, "IPv4 Option", remlen);
 938                         print_ipoptions(data, solen);
 939                         solen = 0;
 940                         break;
 941                 case IP6LS_TT_DEST:
 942                         (void) snprintf(cp, remlen,
 943                             "Destination-Only Data length %u", solen);
 944                         solen = 0;
 945                         break;
 946                 default:
 947                         (void) snprintf(cp, remlen,
 948                             "    unknown %u suboption (len %u)", sotype, solen);
 949                         solen = 0;
 950                         break;
 951                 }
 952                 if (solen != 0) {
 953                         if (printed) {
 954                                 cp = get_line(0, 0);
 955                                 remlen = get_line_remain();
 956                         }
 957                         (void) snprintf(cp, remlen,
 958                             "    malformed %u suboption (remaining %u)",
 959                             sotype, solen);
 960                         data += solen;
 961                 }
 962         }
 963 }
 964 
 965 static void
 966 prt_hbh_options(int flags, const struct ip6_hbh *ipv6ext_hbh)
 967 {
 968         const uint8_t *data, *ndata;
 969         uint32_t len;
 970         uint8_t op_type;
 971         uint8_t op_len;
 972         uint8_t nxt_hdr;
 973 
 974         /* in summary mode, we don't do anything. */
 975         if (flags & F_SUM) {
 976                 return;
 977         }
 978 
 979         show_header("IPv6-HopOpts:  ", "IPv6 Hop-by-Hop Options Header", 0);
 980         show_space();
 981 
 982         /*
 983          * Store the lengh of this ext hdr in bytes.  The caller has
 984          * ensured that there is at least len bytes of data left.
 985          */
 986         len = ipv6ext_hbh->ip6h_len * 8 + 8;
 987 
 988         ndata = (const uint8_t *)ipv6ext_hbh + 2;
 989         len -= 2;
 990 
 991         nxt_hdr = ipv6ext_hbh->ip6h_nxt;
 992         (void) snprintf(get_line(0, 0), get_line_remain(),
 993             "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
 994 
 995         while (len > 0) {
 996                 data = ndata;
 997                 GETINT8(op_type, data);
 998                 /* This is the only one-octet IPv6 option */
 999                 if (op_type == IP6OPT_PAD1) {
1000                         (void) snprintf(get_line(0, 0), get_line_remain(),
1001                             "pad1 option ");
1002                         len--;
1003                         ndata = data;
1004                         continue;
1005                 }
1006                 GETINT8(op_len, data);
1007                 if (len < 2 || op_len + 2 > len) {
1008                         (void) snprintf(get_line(0, 0), get_line_remain(),
1009                             "Error: option %u truncated (%u + 2 > %u)",
1010                             op_type, op_len, len);
1011                         op_len = len - 2;
1012                         /*
1013                          * Continue processing the malformed option so that we
1014                          * can display as much as possible.
1015                          */
1016                 }
1017 
1018                 /* advance pointers to the next option */
1019                 len -= op_len + 2;
1020                 ndata = data + op_len;
1021 
1022                 /* process this option */
1023                 switch (op_type) {
1024                 case IP6OPT_PADN:
1025                         (void) snprintf(get_line(0, 0), get_line_remain(),
1026                             "padN option len = %u", op_len);
1027                         break;
1028                 case IP6OPT_JUMBO: {
1029                         uint32_t payload_len;
1030 
1031                         (void) snprintf(get_line(0, 0), get_line_remain(),
1032                             "Jumbo Payload Option len = %u bytes%s", op_len,
1033                             op_len == sizeof (uint32_t) ? "" : "?");
1034                         if (op_len == sizeof (uint32_t)) {
1035                                 GETINT32(payload_len, data);
1036                                 (void) snprintf(get_line(0, 0),
1037                                     get_line_remain(),
1038                                     "Jumbo Payload Length = %u bytes",
1039                                     payload_len);
1040                         }
1041                         break;
1042                 }
1043                 case IP6OPT_ROUTER_ALERT: {
1044                         uint16_t value;
1045                         const char *label[] = {"MLD", "RSVP", "AN"};
1046 
1047                         (void) snprintf(get_line(0, 0), get_line_remain(),
1048                             "Router Alert Option len = %u bytes%s", op_len,
1049                             op_len == sizeof (uint16_t) ? "" : "?");
1050                         if (op_len == sizeof (uint16_t)) {
1051                                 GETINT16(value, data);
1052                                 (void) snprintf(get_line(0, 0),
1053                                     get_line_remain(),
1054                                     "Alert Type = %d (%s)", value,
1055                                     value < sizeof (label) / sizeof (label[0]) ?
1056                                     label[value] : "???");
1057                         }
1058                         break;
1059                 }
1060                 case IP6OPT_LS:
1061                         print_ip6opt_ls(data, op_len);
1062                         break;
1063                 default:
1064                         (void) snprintf(get_line(0, 0), get_line_remain(),
1065                             "Option type = %u, len = %u", op_type, op_len);
1066                         break;
1067                 }
1068         }
1069 
1070         show_space();
1071 }
1072 
1073 static void
1074 prt_dest_options(int flags, const struct ip6_dest *ipv6ext_dest)
1075 {
1076         const uint8_t *data, *ndata;
1077         uint32_t len;
1078         uint8_t op_type;
1079         uint32_t op_len;
1080         uint8_t nxt_hdr;
1081         uint8_t value;
1082 
1083         /* in summary mode, we don't do anything. */
1084         if (flags & F_SUM) {
1085                 return;
1086         }
1087 
1088         show_header("IPv6-DstOpts:  ", "IPv6 Destination Options Header", 0);
1089         show_space();
1090 
1091         /*
1092          * Store the length of this ext hdr in bytes.  The caller has
1093          * ensured that there is at least len bytes of data left.
1094          */
1095         len = ipv6ext_dest->ip6d_len * 8 + 8;
1096 
1097         ndata = (const uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
1098         len -= 2;
1099 
1100         nxt_hdr = ipv6ext_dest->ip6d_nxt;
1101         (void) snprintf(get_line(0, 0), get_line_remain(),
1102             "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
1103 
1104         while (len > 0) {
1105                 data = ndata;
1106                 GETINT8(op_type, data);
1107                 if (op_type == IP6OPT_PAD1) {
1108                         (void) snprintf(get_line(0, 0), get_line_remain(),
1109                             "pad1 option ");
1110                         len--;
1111                         ndata = data;
1112                         continue;
1113                 }
1114                 GETINT8(op_len, data);
1115                 if (len < 2 || op_len + 2 > len) {
1116                         (void) snprintf(get_line(0, 0), get_line_remain(),
1117                             "Error: option %u truncated (%u + 2 > %u)",
1118                             op_type, op_len, len);
1119                         op_len = len - 2;
1120                         /*
1121                          * Continue processing the malformed option so that we
1122                          * can display as much as possible.
1123                          */
1124                 }
1125 
1126                 /* advance pointers to the next option */
1127                 len -= op_len + 2;
1128                 ndata = data + op_len;
1129 
1130                 /* process this option */
1131                 switch (op_type) {
1132                 case IP6OPT_PADN:
1133                         (void) snprintf(get_line(0, 0), get_line_remain(),
1134                             "padN option len = %u", op_len);
1135                         break;
1136                 case IP6OPT_TUNNEL_LIMIT:
1137                         GETINT8(value, data);
1138                         (void) snprintf(get_line(0, 0), get_line_remain(),
1139                             "tunnel encapsulation limit len = %d, value = %d",
1140                             op_len, value);
1141                         break;
1142                 case IP6OPT_LS:
1143                         print_ip6opt_ls(data, op_len);
1144                         break;
1145                 default:
1146                         (void) snprintf(get_line(0, 0), get_line_remain(),
1147                             "Option type = %u, len = %u", op_type, op_len);
1148                         break;
1149                 }
1150         }
1151 
1152         show_space();
1153 }
1154 
1155 #define ALABEL_MAXLEN   256
1156 
1157 static char ascii_label[ALABEL_MAXLEN];
1158 static char *plabel = ascii_label;
1159 
1160 struct snoop_pair {
1161         int val;
1162         const char *name;
1163 };
1164 
1165 static struct snoop_pair ripso_class_tbl[] = {
1166         TSOL_CL_TOP_SECRET,     "TOP SECRET",
1167         TSOL_CL_SECRET,         "SECRET",
1168         TSOL_CL_CONFIDENTIAL,   "CONFIDENTIAL",
1169         TSOL_CL_UNCLASSIFIED,   "UNCLASSIFIED",
1170         -1,                     NULL
1171 };
1172 
1173 static struct snoop_pair ripso_prot_tbl[] = {
1174         TSOL_PA_GENSER,         "GENSER",
1175         TSOL_PA_SIOP_ESI,       "SIOP-ESI",
1176         TSOL_PA_SCI,            "SCI",
1177         TSOL_PA_NSA,            "NSA",
1178         TSOL_PA_DOE,            "DOE",
1179         0x04,                   "UNASSIGNED",
1180         0x02,                   "UNASSIGNED",
1181         -1,                     NULL
1182 };
1183 
1184 static struct snoop_pair *
1185 get_pair_byval(struct snoop_pair pairlist[], int val)
1186 {
1187         int i;
1188 
1189         for (i = 0; pairlist[i].name != NULL; i++)
1190                 if (pairlist[i].val == val)
1191                         return (&pairlist[i]);
1192         return (NULL);
1193 }
1194 
1195 static void
1196 print_ripso(const uchar_t *opt)
1197 {
1198         struct snoop_pair *ripso_class;
1199         int i, index, prot_len;
1200         boolean_t first_prot;
1201         char line[100], *ptr;
1202 
1203         prot_len = opt[1] - 3;
1204         if (prot_len < 0)
1205                 return;
1206 
1207         show_header("RIPSO:  ", "Revised IP Security Option", 0);
1208         show_space();
1209 
1210         (void) snprintf(get_line(0, 0), get_line_remain(),
1211             "Type = Basic Security Option (%d), Length = %d", opt[0], opt[1]);
1212 
1213         /*
1214          * Display Classification Level
1215          */
1216         ripso_class = get_pair_byval(ripso_class_tbl, (int)opt[2]);
1217         if (ripso_class != NULL)
1218                 (void) snprintf(get_line(0, 0), get_line_remain(),
1219                     "Classification = Unknown (0x%02x)", opt[2]);
1220         else
1221                 (void) snprintf(get_line(0, 0), get_line_remain(),
1222                     "Classification = %s (0x%02x)",
1223                     ripso_class->name, ripso_class->val);
1224 
1225         /*
1226          * Display Protection Authority Flags
1227          */
1228         (void) snprintf(line, sizeof (line), "Protection Authority = ");
1229         ptr = line;
1230         first_prot = B_TRUE;
1231         for (i = 0; i < prot_len; i++) {
1232                 index = 0;
1233                 while (ripso_prot_tbl[index].name != NULL) {
1234                         if (opt[3 + i] & ripso_prot_tbl[index].val) {
1235                                 ptr = strchr(ptr, 0);
1236                                 if (!first_prot) {
1237                                         (void) strlcpy(ptr, ", ",
1238                                             sizeof (line) - (ptr - line));
1239                                         ptr = strchr(ptr, 0);
1240                                 }
1241                                 (void) snprintf(ptr,
1242                                     sizeof (line) - (ptr - line),
1243                                     "%s (0x%02x)",
1244                                     ripso_prot_tbl[index].name,
1245                                     ripso_prot_tbl[index].val);
1246                         }
1247                         index++;
1248                 }
1249                 if ((opt[3 + i] & 1) == 0)
1250                         break;
1251         }
1252         if (!first_prot)
1253                 (void) snprintf(get_line(0, 0), get_line_remain(), "%s", line);
1254         else
1255                 (void) snprintf(get_line(0, 0), get_line_remain(), "%sNone",
1256                     line);
1257 }
1258 
1259 #define CIPSO_GENERIC_ARRAY_LEN 200
1260 
1261 /*
1262  * Return 1 if CIPSO SL and Categories are all 1's; 0 otherwise.
1263  *
1264  * Note: opt starts with "Tag Type":
1265  *
1266  * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1267  *
1268  */
1269 static boolean_t
1270 cipso_high(const uchar_t *opt)
1271 {
1272         int i;
1273 
1274         if (((int)opt[1] + 6) < IP_MAX_OPT_LENGTH)
1275                 return (B_FALSE);
1276         for (i = 0; i < ((int)opt[1] - 3); i++)
1277                 if (opt[3 + i] != 0xff)
1278                         return (B_FALSE);
1279         return (B_TRUE);
1280 }
1281 
1282 /*
1283  * Converts CIPSO label to SL.
1284  *
1285  * Note: opt starts with "Tag Type":
1286  *
1287  * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1288  *
1289  */
1290 static void
1291 cipso2sl(const uchar_t *opt, bslabel_t *sl, int *high)
1292 {
1293         int i, taglen;
1294         uchar_t *q = (uchar_t *)&((_bslabel_impl_t *)sl)->compartments;
1295 
1296         *high = 0;
1297         taglen = opt[1];
1298         memset((caddr_t)sl, 0, sizeof (bslabel_t));
1299 
1300         if (cipso_high(opt)) {
1301                 BSLHIGH(sl);
1302                 *high = 1;
1303         } else {
1304                 LCLASS_SET((_bslabel_impl_t *)sl, opt[3]);
1305                 for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++)
1306                         q[i] = opt[TSOL_TT1_MIN_LENGTH + i];
1307         }
1308         SETBLTYPE(sl, SUN_SL_ID);
1309 }
1310 
1311 static int
1312 interpret_cipso_tagtype1(const uchar_t *opt)
1313 {
1314         int i, taglen, ishigh;
1315         bslabel_t sl;
1316         char line[CIPSO_GENERIC_ARRAY_LEN], *ptr;
1317 
1318         taglen = opt[1];
1319         if (taglen < TSOL_TT1_MIN_LENGTH ||
1320             taglen > TSOL_TT1_MAX_LENGTH)
1321                 return (taglen);
1322 
1323         (void) snprintf(get_line(0, 0), get_line_remain(),
1324             "Tag Type = %d, Tag Length = %d", opt[0], opt[1]);
1325         (void) snprintf(get_line(0, 0), get_line_remain(),
1326             "Sensitivity Level = 0x%02x", opt[3]);
1327         ptr = line;
1328         for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) {
1329                 (void) snprintf(ptr, sizeof (line) - (ptr - line), "%02x",
1330                     opt[TSOL_TT1_MIN_LENGTH + i]);
1331                 ptr = strchr(ptr, 0);
1332         }
1333         if (i != 0) {
1334                 (void) snprintf(get_line(0, 0), get_line_remain(),
1335                     "Categories = ");
1336                 (void) snprintf(get_line(0, 0), get_line_remain(), "\t%s",
1337                     line);
1338         } else {
1339                 (void) snprintf(get_line(0, 0), get_line_remain(),
1340                     "Categories = None");
1341         }
1342         cipso2sl(opt, &sl, &ishigh);
1343         if (is_system_labeled()) {
1344                 if (bsltos(&sl, &plabel, ALABEL_MAXLEN,
1345                     LONG_CLASSIFICATION|LONG_WORDS|VIEW_INTERNAL) < 0) {
1346                         (void) snprintf(get_line(0, 0), get_line_remain(),
1347                             "The Sensitivity Level and Categories can't be "
1348                             "mapped to a valid SL");
1349                 } else {
1350                         (void) snprintf(get_line(0, 0), get_line_remain(),
1351                             "The Sensitivity Level and Categories are mapped "
1352                             "to the SL:");
1353                         (void) snprintf(get_line(0, 0), get_line_remain(),
1354                             "\t%s", ascii_label);
1355                 }
1356         }
1357         return (taglen);
1358 }
1359 
1360 /*
1361  * The following struct definition #define's are copied from TS1.x. They are
1362  * not used here (except TTYPE_3_MAX_TOKENS), but included as a reference for
1363  * the tag type 3 packet format.
1364  */
1365 #define TTYPE_3_MAX_TOKENS      7
1366 
1367 /*
1368  * Display CIPSO tag type 3 which is defined by MAXSIX.
1369  */
1370 static int
1371 interpret_cipso_tagtype3(const uchar_t *opt)
1372 {
1373         uchar_t tagtype;
1374         int index, numtokens, taglen;
1375         uint16_t mask;
1376         uint32_t token;
1377         static const char *name[] = {
1378                 "SL",
1379                 "NCAV",
1380                 "INTEG",
1381                 "SID",
1382                 "undefined",
1383                 "undefined",
1384                 "IL",
1385                 "PRIVS",
1386                 "LUID",
1387                 "PID",
1388                 "IDS",
1389                 "ACL"
1390         };
1391 
1392         tagtype = *opt++;
1393         (void) memcpy(&mask, opt + 3, sizeof (mask));
1394         (void) snprintf(get_line(0, 0), get_line_remain(),
1395             "Tag Type = %d (MAXSIX)", tagtype);
1396         (void) snprintf(get_line(0, 0), get_line_remain(),
1397             "Generation = 0x%02x%02x%02x, Mask = 0x%04x", opt[0], opt[1],
1398             opt[2], mask);
1399         opt += 3 + sizeof (mask);
1400 
1401         /*
1402          * Display tokens
1403          */
1404         numtokens = 0;
1405         index = 0;
1406         while (mask != 0 && numtokens < TTYPE_3_MAX_TOKENS) {
1407                 if (mask & 0x0001) {
1408                         (void) memcpy(&token, opt, sizeof (token));
1409                         opt += sizeof (token);
1410                         (void) snprintf(get_line(0, 0), get_line_remain(),
1411                             "Attribute = %s, Token = 0x%08x",
1412                             index < sizeof (name) / sizeof (*name) ?
1413                             name[index] : "unknown", token);
1414                         numtokens++;
1415                 }
1416                 mask = mask >> 1;
1417                 index++;
1418         }
1419 
1420         taglen = 6 + numtokens * 4;
1421         return (taglen);
1422 }
1423 
1424 static void
1425 print_cipso(const uchar_t *opt)
1426 {
1427         int optlen, taglen, tagnum;
1428         uint32_t doi;
1429         char line[CIPSO_GENERIC_ARRAY_LEN];
1430         char *oldnest;
1431 
1432         optlen = opt[1];
1433         if (optlen < TSOL_CIPSO_MIN_LENGTH || optlen > TSOL_CIPSO_MAX_LENGTH)
1434                 return;
1435 
1436         oldnest = prot_nest_prefix;
1437         prot_nest_prefix = prot_prefix;
1438         show_header("CIPSO:  ", "Common IP Security Option", 0);
1439         show_space();
1440 
1441         /*
1442          * Display CIPSO Header
1443          */
1444         (void) snprintf(get_line(0, 0), get_line_remain(),
1445             "Type = CIPSO (%d), Length = %d", opt[0], opt[1]);
1446         (void) memcpy(&doi, opt + 2, sizeof (doi));
1447         (void) snprintf(get_line(0, 0), get_line_remain(),
1448             "Domain of Interpretation = %u", (unsigned)ntohl(doi));
1449 
1450         if (opt[1] == TSOL_CIPSO_MIN_LENGTH) {  /* no tags */
1451                 show_space();
1452                 prot_prefix = prot_nest_prefix;
1453                 prot_nest_prefix = oldnest;
1454                 return;
1455         }
1456         optlen -= TSOL_CIPSO_MIN_LENGTH;
1457         opt += TSOL_CIPSO_MIN_LENGTH;
1458 
1459         /*
1460          * Display Each Tag
1461          */
1462         tagnum = 1;
1463         while (optlen >= TSOL_TT1_MIN_LENGTH) {
1464                 (void) snprintf(line, sizeof (line), "Tag# %d", tagnum);
1465                 show_header("CIPSO:  ", line, 0);
1466                 /*
1467                  * We handle tag type 1 and 3 only. Note, tag type 3
1468                  * is MAXSIX defined.
1469                  */
1470                 switch (opt[0]) {
1471                 case 1:
1472                         taglen = interpret_cipso_tagtype1(opt);
1473                         break;
1474                 case 3:
1475                         taglen = interpret_cipso_tagtype3(opt);
1476                         break;
1477                 default:
1478                         (void) snprintf(get_line(0, 0), get_line_remain(),
1479                             "Unknown Tag Type %d", opt[0]);
1480                         show_space();
1481                         prot_prefix = prot_nest_prefix;
1482                         prot_nest_prefix = oldnest;
1483                         return;
1484                 }
1485 
1486                 /*
1487                  * Move to the next tag
1488                  */
1489                 if (taglen <= 0)
1490                         break;
1491                 optlen -= taglen;
1492                 opt += taglen;
1493                 tagnum++;
1494         }
1495         show_space();
1496         prot_prefix = prot_nest_prefix;
1497         prot_nest_prefix = oldnest;
1498 }