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