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         default:                return ("");
 742         }
 743 }
 744 
 745 static void
 746 prt_routing_hdr(int flags, const struct ip6_rthdr *ipv6ext_rthdr)
 747 {
 748         uint8_t nxt_hdr;
 749         uint8_t type;
 750         uint32_t len;
 751         uint8_t segleft;
 752         uint32_t numaddrs;
 753         int i;
 754         struct ip6_rthdr0 *ipv6ext_rthdr0;
 755         struct in6_addr *addrs;
 756         char addr[INET6_ADDRSTRLEN];
 757 
 758         /* in summary mode, we don't do anything. */
 759         if (flags & F_SUM) {
 760                 return;
 761         }
 762 
 763         nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
 764         type = ipv6ext_rthdr->ip6r_type;
 765         len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
 766         segleft = ipv6ext_rthdr->ip6r_segleft;
 767 
 768         show_header("IPv6-Route:  ", "IPv6 Routing Header", 0);
 769         show_space();
 770 
 771         (void) snprintf(get_line(0, 0), get_line_remain(),
 772             "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
 773         (void) snprintf(get_line(0, 0), get_line_remain(),
 774             "Header length = %d", len);
 775         (void) snprintf(get_line(0, 0), get_line_remain(),
 776             "Routing type = %d", type);
 777         (void) snprintf(get_line(0, 0), get_line_remain(),
 778             "Segments left = %d", segleft);
 779 
 780         if (type == IPV6_RTHDR_TYPE_0) {
 781                 /*
 782                  * XXX This loop will print all addresses in the routing header,
 783                  * XXX not just the segments left.
 784                  * XXX (The header length field is twice the number of
 785                  * XXX addresses)
 786                  * XXX At some future time, we may want to change this
 787                  * XXX to differentiate between the hops yet to do
 788                  * XXX and the hops already taken.
 789                  */
 790                 /* LINTED: alignment */
 791                 ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
 792                 numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
 793                 addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
 794                 for (i = 0; i < numaddrs; i++) {
 795                         (void) inet_ntop(AF_INET6, &addrs[i], addr,
 796                             INET6_ADDRSTRLEN);
 797                         (void) snprintf(get_line(0, 0), get_line_remain(),
 798                             "address[%d]=%s", i, addr);
 799                 }
 800         }
 801 
 802         show_space();
 803 }
 804 
 805 static void
 806 prt_fragment_hdr(int flags, const struct ip6_frag *ipv6ext_frag)
 807 {
 808         boolean_t morefrag;
 809         uint16_t fragoffset;
 810         uint8_t nxt_hdr;
 811         uint32_t fragident;
 812 
 813         /* extract the various fields from the fragment header */
 814         nxt_hdr = ipv6ext_frag->ip6f_nxt;
 815         morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
 816             ? B_FALSE : B_TRUE;
 817         fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
 818         fragident = ntohl(ipv6ext_frag->ip6f_ident);
 819 
 820         if (flags & F_SUM) {
 821                 (void) snprintf(get_sum_line(), MAXLINE,
 822                     "IPv6 fragment ID=%u Offset=%-4d MF=%d",
 823                     fragident,
 824                     fragoffset,
 825                     morefrag);
 826         } else { /* F_DTAIL */
 827                 show_header("IPv6-Frag:  ", "IPv6 Fragment Header", 0);
 828                 show_space();
 829 
 830                 (void) snprintf(get_line(0, 0), get_line_remain(),
 831                     "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
 832                 (void) snprintf(get_line(0, 0), get_line_remain(),
 833                     "Fragment Offset = %d", fragoffset);
 834                 (void) snprintf(get_line(0, 0), get_line_remain(),
 835                     "More Fragments Flag = %s", morefrag ? "true" : "false");
 836                 (void) snprintf(get_line(0, 0), get_line_remain(),
 837                     "Identification = %u", fragident);
 838 
 839                 show_space();
 840         }
 841 }
 842 
 843 static void
 844 print_ip6opt_ls(const uchar_t *data, unsigned int op_len)
 845 {
 846         uint32_t doi;
 847         uint8_t sotype, solen;
 848         uint16_t value, value2;
 849         char *cp;
 850         int remlen;
 851         boolean_t printed;
 852 
 853         (void) snprintf(get_line(0, 0), get_line_remain(),
 854             "Labeled Security Option len = %u bytes%s", op_len,
 855             op_len < sizeof (uint32_t) || (op_len & 1) != 0 ? "?" : "");
 856         if (op_len < sizeof (uint32_t))
 857                 return;
 858         GETINT32(doi, data);
 859         (void) snprintf(get_line(0, 0), get_line_remain(),
 860             "    DOI = %d (%s)", doi, doi == IP6LS_DOI_V4 ? "IPv4" : "???");
 861         op_len -= sizeof (uint32_t);
 862         while (op_len > 0) {
 863                 GETINT8(sotype, data);
 864                 if (op_len < 2) {
 865                         (void) snprintf(get_line(0, 0), get_line_remain(),
 866                             "    truncated %u suboption (no len)", sotype);
 867                         break;
 868                 }
 869                 GETINT8(solen, data);
 870                 if (solen < 2 || solen > op_len) {
 871                         (void) snprintf(get_line(0, 0), get_line_remain(),
 872                             "    bad %u suboption (len 2 <= %u <= %u)",
 873                             sotype, solen, op_len);
 874                         if (solen < 2)
 875                                 solen = 2;
 876                         if (solen > op_len)
 877                                 solen = op_len;
 878                 }
 879                 op_len -= solen;
 880                 solen -= 2;
 881                 cp = get_line(0, 0);
 882                 remlen = get_line_remain();
 883                 (void) strlcpy(cp, "    ", remlen);
 884                 cp += 4;
 885                 remlen -= 4;
 886                 printed = B_TRUE;
 887                 switch (sotype) {
 888                 case IP6LS_TT_LEVEL:
 889                         if (solen != 2) {
 890                                 printed = B_FALSE;
 891                                 break;
 892                         }
 893                         GETINT16(value, data);
 894                         (void) snprintf(cp, remlen, "Level %u", value);
 895                         solen = 0;
 896                         break;
 897                 case IP6LS_TT_VECTOR:
 898                         (void) strlcpy(cp, "Bit-Vector: ", remlen);
 899                         remlen -= strlen(cp);
 900                         cp += strlen(cp);
 901                         while (solen > 1) {
 902                                 GETINT16(value, data);
 903                                 solen -= 2;
 904                                 (void) snprintf(cp, remlen, "%04x", value);
 905                                 remlen -= strlen(cp);
 906                                 cp += strlen(cp);
 907                         }
 908                         break;
 909                 case IP6LS_TT_ENUM:
 910                         (void) strlcpy(cp, "Enumeration:", remlen);
 911                         remlen -= strlen(cp);
 912                         cp += strlen(cp);
 913                         while (solen > 1) {
 914                                 GETINT16(value, data);
 915                                 solen -= 2;
 916                                 (void) snprintf(cp, remlen, " %u", value);
 917                                 remlen -= strlen(cp);
 918                                 cp += strlen(cp);
 919                         }
 920                         break;
 921                 case IP6LS_TT_RANGES:
 922                         (void) strlcpy(cp, "Ranges:", remlen);
 923                         remlen -= strlen(cp);
 924                         cp += strlen(cp);
 925                         while (solen > 3) {
 926                                 GETINT16(value, data);
 927                                 GETINT16(value2, data);
 928                                 solen -= 4;
 929                                 (void) snprintf(cp, remlen, " %u-%u", value,
 930                                     value2);
 931                                 remlen -= strlen(cp);
 932                                 cp += strlen(cp);
 933                         }
 934                         break;
 935                 case IP6LS_TT_V4:
 936                         (void) strlcpy(cp, "IPv4 Option", remlen);
 937                         print_ipoptions(data, solen);
 938                         solen = 0;
 939                         break;
 940                 case IP6LS_TT_DEST:
 941                         (void) snprintf(cp, remlen,
 942                             "Destination-Only Data length %u", solen);
 943                         solen = 0;
 944                         break;
 945                 default:
 946                         (void) snprintf(cp, remlen,
 947                             "    unknown %u suboption (len %u)", sotype, solen);
 948                         solen = 0;
 949                         break;
 950                 }
 951                 if (solen != 0) {
 952                         if (printed) {
 953                                 cp = get_line(0, 0);
 954                                 remlen = get_line_remain();
 955                         }
 956                         (void) snprintf(cp, remlen,
 957                             "    malformed %u suboption (remaining %u)",
 958                             sotype, solen);
 959                         data += solen;
 960                 }
 961         }
 962 }
 963 
 964 static void
 965 prt_hbh_options(int flags, const struct ip6_hbh *ipv6ext_hbh)
 966 {
 967         const uint8_t *data, *ndata;
 968         uint32_t len;
 969         uint8_t op_type;
 970         uint8_t op_len;
 971         uint8_t nxt_hdr;
 972 
 973         /* in summary mode, we don't do anything. */
 974         if (flags & F_SUM) {
 975                 return;
 976         }
 977 
 978         show_header("IPv6-HopOpts:  ", "IPv6 Hop-by-Hop Options Header", 0);
 979         show_space();
 980 
 981         /*
 982          * Store the lengh of this ext hdr in bytes.  The caller has
 983          * ensured that there is at least len bytes of data left.
 984          */
 985         len = ipv6ext_hbh->ip6h_len * 8 + 8;
 986 
 987         ndata = (const uint8_t *)ipv6ext_hbh + 2;
 988         len -= 2;
 989 
 990         nxt_hdr = ipv6ext_hbh->ip6h_nxt;
 991         (void) snprintf(get_line(0, 0), get_line_remain(),
 992             "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
 993 
 994         while (len > 0) {
 995                 data = ndata;
 996                 GETINT8(op_type, data);
 997                 /* This is the only one-octet IPv6 option */
 998                 if (op_type == IP6OPT_PAD1) {
 999                         (void) snprintf(get_line(0, 0), get_line_remain(),
1000                             "pad1 option ");
1001                         len--;
1002                         ndata = data;
1003                         continue;
1004                 }
1005                 GETINT8(op_len, data);
1006                 if (len < 2 || op_len + 2 > len) {
1007                         (void) snprintf(get_line(0, 0), get_line_remain(),
1008                             "Error: option %u truncated (%u + 2 > %u)",
1009                             op_type, op_len, len);
1010                         op_len = len - 2;
1011                         /*
1012                          * Continue processing the malformed option so that we
1013                          * can display as much as possible.
1014                          */
1015                 }
1016 
1017                 /* advance pointers to the next option */
1018                 len -= op_len + 2;
1019                 ndata = data + op_len;
1020 
1021                 /* process this option */
1022                 switch (op_type) {
1023                 case IP6OPT_PADN:
1024                         (void) snprintf(get_line(0, 0), get_line_remain(),
1025                             "padN option len = %u", op_len);
1026                         break;
1027                 case IP6OPT_JUMBO: {
1028                         uint32_t payload_len;
1029 
1030                         (void) snprintf(get_line(0, 0), get_line_remain(),
1031                             "Jumbo Payload Option len = %u bytes%s", op_len,
1032                             op_len == sizeof (uint32_t) ? "" : "?");
1033                         if (op_len == sizeof (uint32_t)) {
1034                                 GETINT32(payload_len, data);
1035                                 (void) snprintf(get_line(0, 0),
1036                                     get_line_remain(),
1037                                     "Jumbo Payload Length = %u bytes",
1038                                     payload_len);
1039                         }
1040                         break;
1041                 }
1042                 case IP6OPT_ROUTER_ALERT: {
1043                         uint16_t value;
1044                         const char *label[] = {"MLD", "RSVP", "AN"};
1045 
1046                         (void) snprintf(get_line(0, 0), get_line_remain(),
1047                             "Router Alert Option len = %u bytes%s", op_len,
1048                             op_len == sizeof (uint16_t) ? "" : "?");
1049                         if (op_len == sizeof (uint16_t)) {
1050                                 GETINT16(value, data);
1051                                 (void) snprintf(get_line(0, 0),
1052                                     get_line_remain(),
1053                                     "Alert Type = %d (%s)", value,
1054                                     value < sizeof (label) / sizeof (label[0]) ?
1055                                     label[value] : "???");
1056                         }
1057                         break;
1058                 }
1059                 case IP6OPT_LS:
1060                         print_ip6opt_ls(data, op_len);
1061                         break;
1062                 default:
1063                         (void) snprintf(get_line(0, 0), get_line_remain(),
1064                             "Option type = %u, len = %u", op_type, op_len);
1065                         break;
1066                 }
1067         }
1068 
1069         show_space();
1070 }
1071 
1072 static void
1073 prt_dest_options(int flags, const struct ip6_dest *ipv6ext_dest)
1074 {
1075         const uint8_t *data, *ndata;
1076         uint32_t len;
1077         uint8_t op_type;
1078         uint32_t op_len;
1079         uint8_t nxt_hdr;
1080         uint8_t value;
1081 
1082         /* in summary mode, we don't do anything. */
1083         if (flags & F_SUM) {
1084                 return;
1085         }
1086 
1087         show_header("IPv6-DstOpts:  ", "IPv6 Destination Options Header", 0);
1088         show_space();
1089 
1090         /*
1091          * Store the length of this ext hdr in bytes.  The caller has
1092          * ensured that there is at least len bytes of data left.
1093          */
1094         len = ipv6ext_dest->ip6d_len * 8 + 8;
1095 
1096         ndata = (const uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
1097         len -= 2;
1098 
1099         nxt_hdr = ipv6ext_dest->ip6d_nxt;
1100         (void) snprintf(get_line(0, 0), get_line_remain(),
1101             "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
1102 
1103         while (len > 0) {
1104                 data = ndata;
1105                 GETINT8(op_type, data);
1106                 if (op_type == IP6OPT_PAD1) {
1107                         (void) snprintf(get_line(0, 0), get_line_remain(),
1108                             "pad1 option ");
1109                         len--;
1110                         ndata = data;
1111                         continue;
1112                 }
1113                 GETINT8(op_len, data);
1114                 if (len < 2 || op_len + 2 > len) {
1115                         (void) snprintf(get_line(0, 0), get_line_remain(),
1116                             "Error: option %u truncated (%u + 2 > %u)",
1117                             op_type, op_len, len);
1118                         op_len = len - 2;
1119                         /*
1120                          * Continue processing the malformed option so that we
1121                          * can display as much as possible.
1122                          */
1123                 }
1124 
1125                 /* advance pointers to the next option */
1126                 len -= op_len + 2;
1127                 ndata = data + op_len;
1128 
1129                 /* process this option */
1130                 switch (op_type) {
1131                 case IP6OPT_PADN:
1132                         (void) snprintf(get_line(0, 0), get_line_remain(),
1133                             "padN option len = %u", op_len);
1134                         break;
1135                 case IP6OPT_TUNNEL_LIMIT:
1136                         GETINT8(value, data);
1137                         (void) snprintf(get_line(0, 0), get_line_remain(),
1138                             "tunnel encapsulation limit len = %d, value = %d",
1139                             op_len, value);
1140                         break;
1141                 case IP6OPT_LS:
1142                         print_ip6opt_ls(data, op_len);
1143                         break;
1144                 default:
1145                         (void) snprintf(get_line(0, 0), get_line_remain(),
1146                             "Option type = %u, len = %u", op_type, op_len);
1147                         break;
1148                 }
1149         }
1150 
1151         show_space();
1152 }
1153 
1154 #define ALABEL_MAXLEN   256
1155 
1156 static char ascii_label[ALABEL_MAXLEN];
1157 static char *plabel = ascii_label;
1158 
1159 struct snoop_pair {
1160         int val;
1161         const char *name;
1162 };
1163 
1164 static struct snoop_pair ripso_class_tbl[] = {
1165         TSOL_CL_TOP_SECRET,     "TOP SECRET",
1166         TSOL_CL_SECRET,         "SECRET",
1167         TSOL_CL_CONFIDENTIAL,   "CONFIDENTIAL",
1168         TSOL_CL_UNCLASSIFIED,   "UNCLASSIFIED",
1169         -1,                     NULL
1170 };
1171 
1172 static struct snoop_pair ripso_prot_tbl[] = {
1173         TSOL_PA_GENSER,         "GENSER",
1174         TSOL_PA_SIOP_ESI,       "SIOP-ESI",
1175         TSOL_PA_SCI,            "SCI",
1176         TSOL_PA_NSA,            "NSA",
1177         TSOL_PA_DOE,            "DOE",
1178         0x04,                   "UNASSIGNED",
1179         0x02,                   "UNASSIGNED",
1180         -1,                     NULL
1181 };
1182 
1183 static struct snoop_pair *
1184 get_pair_byval(struct snoop_pair pairlist[], int val)
1185 {
1186         int i;
1187 
1188         for (i = 0; pairlist[i].name != NULL; i++)
1189                 if (pairlist[i].val == val)
1190                         return (&pairlist[i]);
1191         return (NULL);
1192 }
1193 
1194 static void
1195 print_ripso(const uchar_t *opt)
1196 {
1197         struct snoop_pair *ripso_class;
1198         int i, index, prot_len;
1199         boolean_t first_prot;
1200         char line[100], *ptr;
1201 
1202         prot_len = opt[1] - 3;
1203         if (prot_len < 0)
1204                 return;
1205 
1206         show_header("RIPSO:  ", "Revised IP Security Option", 0);
1207         show_space();
1208 
1209         (void) snprintf(get_line(0, 0), get_line_remain(),
1210             "Type = Basic Security Option (%d), Length = %d", opt[0], opt[1]);
1211 
1212         /*
1213          * Display Classification Level
1214          */
1215         ripso_class = get_pair_byval(ripso_class_tbl, (int)opt[2]);
1216         if (ripso_class != NULL)
1217                 (void) snprintf(get_line(0, 0), get_line_remain(),
1218                     "Classification = Unknown (0x%02x)", opt[2]);
1219         else
1220                 (void) snprintf(get_line(0, 0), get_line_remain(),
1221                     "Classification = %s (0x%02x)",
1222                     ripso_class->name, ripso_class->val);
1223 
1224         /*
1225          * Display Protection Authority Flags
1226          */
1227         (void) snprintf(line, sizeof (line), "Protection Authority = ");
1228         ptr = line;
1229         first_prot = B_TRUE;
1230         for (i = 0; i < prot_len; i++) {
1231                 index = 0;
1232                 while (ripso_prot_tbl[index].name != NULL) {
1233                         if (opt[3 + i] & ripso_prot_tbl[index].val) {
1234                                 ptr = strchr(ptr, 0);
1235                                 if (!first_prot) {
1236                                         (void) strlcpy(ptr, ", ",
1237                                             sizeof (line) - (ptr - line));
1238                                         ptr = strchr(ptr, 0);
1239                                 }
1240                                 (void) snprintf(ptr,
1241                                     sizeof (line) - (ptr - line),
1242                                     "%s (0x%02x)",
1243                                     ripso_prot_tbl[index].name,
1244                                     ripso_prot_tbl[index].val);
1245                         }
1246                         index++;
1247                 }
1248                 if ((opt[3 + i] & 1) == 0)
1249                         break;
1250         }
1251         if (!first_prot)
1252                 (void) snprintf(get_line(0, 0), get_line_remain(), "%s", line);
1253         else
1254                 (void) snprintf(get_line(0, 0), get_line_remain(), "%sNone",
1255                     line);
1256 }
1257 
1258 #define CIPSO_GENERIC_ARRAY_LEN 200
1259 
1260 /*
1261  * Return 1 if CIPSO SL and Categories are all 1's; 0 otherwise.
1262  *
1263  * Note: opt starts with "Tag Type":
1264  *
1265  * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1266  *
1267  */
1268 static boolean_t
1269 cipso_high(const uchar_t *opt)
1270 {
1271         int i;
1272 
1273         if (((int)opt[1] + 6) < IP_MAX_OPT_LENGTH)
1274                 return (B_FALSE);
1275         for (i = 0; i < ((int)opt[1] - 3); i++)
1276                 if (opt[3 + i] != 0xff)
1277                         return (B_FALSE);
1278         return (B_TRUE);
1279 }
1280 
1281 /*
1282  * Converts CIPSO label to SL.
1283  *
1284  * Note: opt starts with "Tag Type":
1285  *
1286  * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
1287  *
1288  */
1289 static void
1290 cipso2sl(const uchar_t *opt, bslabel_t *sl, int *high)
1291 {
1292         int i, taglen;
1293         uchar_t *q = (uchar_t *)&((_bslabel_impl_t *)sl)->compartments;
1294 
1295         *high = 0;
1296         taglen = opt[1];
1297         memset((caddr_t)sl, 0, sizeof (bslabel_t));
1298 
1299         if (cipso_high(opt)) {
1300                 BSLHIGH(sl);
1301                 *high = 1;
1302         } else {
1303                 LCLASS_SET((_bslabel_impl_t *)sl, opt[3]);
1304                 for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++)
1305                         q[i] = opt[TSOL_TT1_MIN_LENGTH + i];
1306         }
1307         SETBLTYPE(sl, SUN_SL_ID);
1308 }
1309 
1310 static int
1311 interpret_cipso_tagtype1(const uchar_t *opt)
1312 {
1313         int i, taglen, ishigh;
1314         bslabel_t sl;
1315         char line[CIPSO_GENERIC_ARRAY_LEN], *ptr;
1316 
1317         taglen = opt[1];
1318         if (taglen < TSOL_TT1_MIN_LENGTH ||
1319             taglen > TSOL_TT1_MAX_LENGTH)
1320                 return (taglen);
1321 
1322         (void) snprintf(get_line(0, 0), get_line_remain(),
1323             "Tag Type = %d, Tag Length = %d", opt[0], opt[1]);
1324         (void) snprintf(get_line(0, 0), get_line_remain(),
1325             "Sensitivity Level = 0x%02x", opt[3]);
1326         ptr = line;
1327         for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) {
1328                 (void) snprintf(ptr, sizeof (line) - (ptr - line), "%02x",
1329                     opt[TSOL_TT1_MIN_LENGTH + i]);
1330                 ptr = strchr(ptr, 0);
1331         }
1332         if (i != 0) {
1333                 (void) snprintf(get_line(0, 0), get_line_remain(),
1334                     "Categories = ");
1335                 (void) snprintf(get_line(0, 0), get_line_remain(), "\t%s",
1336                     line);
1337         } else {
1338                 (void) snprintf(get_line(0, 0), get_line_remain(),
1339                     "Categories = None");
1340         }
1341         cipso2sl(opt, &sl, &ishigh);
1342         if (is_system_labeled()) {
1343                 if (bsltos(&sl, &plabel, ALABEL_MAXLEN,
1344                     LONG_CLASSIFICATION|LONG_WORDS|VIEW_INTERNAL) < 0) {
1345                         (void) snprintf(get_line(0, 0), get_line_remain(),
1346                             "The Sensitivity Level and Categories can't be "
1347                             "mapped to a valid SL");
1348                 } else {
1349                         (void) snprintf(get_line(0, 0), get_line_remain(),
1350                             "The Sensitivity Level and Categories are mapped "
1351                             "to the SL:");
1352                         (void) snprintf(get_line(0, 0), get_line_remain(),
1353                             "\t%s", ascii_label);
1354                 }
1355         }
1356         return (taglen);
1357 }
1358 
1359 /*
1360  * The following struct definition #define's are copied from TS1.x. They are
1361  * not used here (except TTYPE_3_MAX_TOKENS), but included as a reference for
1362  * the tag type 3 packet format.
1363  */
1364 #define TTYPE_3_MAX_TOKENS      7
1365 
1366 /*
1367  * Display CIPSO tag type 3 which is defined by MAXSIX.
1368  */
1369 static int
1370 interpret_cipso_tagtype3(const uchar_t *opt)
1371 {
1372         uchar_t tagtype;
1373         int index, numtokens, taglen;
1374         uint16_t mask;
1375         uint32_t token;
1376         static const char *name[] = {
1377                 "SL",
1378                 "NCAV",
1379                 "INTEG",
1380                 "SID",
1381                 "undefined",
1382                 "undefined",
1383                 "IL",
1384                 "PRIVS",
1385                 "LUID",
1386                 "PID",
1387                 "IDS",
1388                 "ACL"
1389         };
1390 
1391         tagtype = *opt++;
1392         (void) memcpy(&mask, opt + 3, sizeof (mask));
1393         (void) snprintf(get_line(0, 0), get_line_remain(),
1394             "Tag Type = %d (MAXSIX)", tagtype);
1395         (void) snprintf(get_line(0, 0), get_line_remain(),
1396             "Generation = 0x%02x%02x%02x, Mask = 0x%04x", opt[0], opt[1],
1397             opt[2], mask);
1398         opt += 3 + sizeof (mask);
1399 
1400         /*
1401          * Display tokens
1402          */
1403         numtokens = 0;
1404         index = 0;
1405         while (mask != 0 && numtokens < TTYPE_3_MAX_TOKENS) {
1406                 if (mask & 0x0001) {
1407                         (void) memcpy(&token, opt, sizeof (token));
1408                         opt += sizeof (token);
1409                         (void) snprintf(get_line(0, 0), get_line_remain(),
1410                             "Attribute = %s, Token = 0x%08x",
1411                             index < sizeof (name) / sizeof (*name) ?
1412                             name[index] : "unknown", token);
1413                         numtokens++;
1414                 }
1415                 mask = mask >> 1;
1416                 index++;
1417         }
1418 
1419         taglen = 6 + numtokens * 4;
1420         return (taglen);
1421 }
1422 
1423 static void
1424 print_cipso(const uchar_t *opt)
1425 {
1426         int optlen, taglen, tagnum;
1427         uint32_t doi;
1428         char line[CIPSO_GENERIC_ARRAY_LEN];
1429         char *oldnest;
1430 
1431         optlen = opt[1];
1432         if (optlen < TSOL_CIPSO_MIN_LENGTH || optlen > TSOL_CIPSO_MAX_LENGTH)
1433                 return;
1434 
1435         oldnest = prot_nest_prefix;
1436         prot_nest_prefix = prot_prefix;
1437         show_header("CIPSO:  ", "Common IP Security Option", 0);
1438         show_space();
1439 
1440         /*
1441          * Display CIPSO Header
1442          */
1443         (void) snprintf(get_line(0, 0), get_line_remain(),
1444             "Type = CIPSO (%d), Length = %d", opt[0], opt[1]);
1445         (void) memcpy(&doi, opt + 2, sizeof (doi));
1446         (void) snprintf(get_line(0, 0), get_line_remain(),
1447             "Domain of Interpretation = %u", (unsigned)ntohl(doi));
1448 
1449         if (opt[1] == TSOL_CIPSO_MIN_LENGTH) {  /* no tags */
1450                 show_space();
1451                 prot_prefix = prot_nest_prefix;
1452                 prot_nest_prefix = oldnest;
1453                 return;
1454         }
1455         optlen -= TSOL_CIPSO_MIN_LENGTH;
1456         opt += TSOL_CIPSO_MIN_LENGTH;
1457 
1458         /*
1459          * Display Each Tag
1460          */
1461         tagnum = 1;
1462         while (optlen >= TSOL_TT1_MIN_LENGTH) {
1463                 (void) snprintf(line, sizeof (line), "Tag# %d", tagnum);
1464                 show_header("CIPSO:  ", line, 0);
1465                 /*
1466                  * We handle tag type 1 and 3 only. Note, tag type 3
1467                  * is MAXSIX defined.
1468                  */
1469                 switch (opt[0]) {
1470                 case 1:
1471                         taglen = interpret_cipso_tagtype1(opt);
1472                         break;
1473                 case 3:
1474                         taglen = interpret_cipso_tagtype3(opt);
1475                         break;
1476                 default:
1477                         (void) snprintf(get_line(0, 0), get_line_remain(),
1478                             "Unknown Tag Type %d", opt[0]);
1479                         show_space();
1480                         prot_prefix = prot_nest_prefix;
1481                         prot_nest_prefix = oldnest;
1482                         return;
1483                 }
1484 
1485                 /*
1486                  * Move to the next tag
1487                  */
1488                 if (taglen <= 0)
1489                         break;
1490                 optlen -= taglen;
1491                 opt += taglen;
1492                 tagnum++;
1493         }
1494         show_space();
1495         prot_prefix = prot_nest_prefix;
1496         prot_nest_prefix = oldnest;
1497 }