diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c | 1490 |
1 files changed, 1490 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c new file mode 100644 index 0000000..af37d85 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c @@ -0,0 +1,1490 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2012 Milan Jurik. All rights reserved. + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#include <sys/stropts.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <netinet/if_ether.h> +#include <inet/ip.h> +#include <inet/ip6.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <tsol/label.h> +#include <sys/tsol/tndb.h> +#include <sys/tsol/label_macro.h> + +#include "snoop.h" + + +/* + * IPv6 extension header masks. These are used by the print_ipv6_extensions() + * function to return information to the caller about which extension headers + * were processed. This can be useful if the caller wants to know if the + * packet is an IPv6 fragment, for example. + */ +#define SNOOP_HOPOPTS 0x01U +#define SNOOP_ROUTING 0x02U +#define SNOOP_DSTOPTS 0x04U +#define SNOOP_FRAGMENT 0x08U +#define SNOOP_AH 0x10U +#define SNOOP_ESP 0x20U +#define SNOOP_IPV6 0x40U + +static void prt_routing_hdr(int, const struct ip6_rthdr *); +static void prt_fragment_hdr(int, const struct ip6_frag *); +static void prt_hbh_options(int, const struct ip6_hbh *); +static void prt_dest_options(int, const struct ip6_dest *); +static void print_route(const uchar_t *); +static void print_ipoptions(const uchar_t *, int); +static void print_ripso(const uchar_t *); +static void print_cipso(const uchar_t *); + +/* Keep track of how many nested IP headers we have. */ +unsigned int encap_levels; +unsigned int total_encap_levels = 1; + +int +interpret_ip(int flags, const struct ip *ip, int fraglen) +{ + uchar_t *data; + char buff[24]; + boolean_t isfrag = B_FALSE; + boolean_t morefrag; + uint16_t fragoffset; + int hdrlen; + uint16_t iplen, uitmp; + + if (ip->ip_v == IPV6_VERSION) { + iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen); + return (iplen); + } + + if (encap_levels == 0) + total_encap_levels = 0; + encap_levels++; + total_encap_levels++; + + hdrlen = ip->ip_hl * 4; + data = ((uchar_t *)ip) + hdrlen; + iplen = ntohs(ip->ip_len) - hdrlen; + fraglen -= hdrlen; + if (fraglen > iplen) + fraglen = iplen; + if (fraglen < 0) { + (void) snprintf(get_sum_line(), MAXLINE, + "IP truncated: header missing %d bytes", -fraglen); + encap_levels--; + return (fraglen + iplen); + } + /* + * We flag this as a fragment if the more fragments bit is set, or + * if the fragment offset is non-zero. + */ + morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE; + fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8; + if (morefrag || fragoffset != 0) + isfrag = B_TRUE; + + src_name = addrtoname(AF_INET, &ip->ip_src); + dst_name = addrtoname(AF_INET, &ip->ip_dst); + + if (flags & F_SUM) { + if (isfrag) { + (void) snprintf(get_sum_line(), MAXLINE, + "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x " + "TTL=%d", + getproto(ip->ip_p), + ntohs(ip->ip_id), + fragoffset, + morefrag, + ip->ip_tos, + ip->ip_ttl); + } else { + (void) strlcpy(buff, inet_ntoa(ip->ip_dst), + sizeof (buff)); + uitmp = ntohs(ip->ip_len); + (void) snprintf(get_sum_line(), MAXLINE, + "IP D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d", + buff, + inet_ntoa(ip->ip_src), + uitmp, + iplen > fraglen ? "?" : "", + ntohs(ip->ip_id), + ip->ip_tos, + ip->ip_ttl); + } + } + + if (flags & F_DTAIL) { + show_header("IP: ", "IP Header", iplen); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Version = %d", ip->ip_v); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Header length = %d bytes", hdrlen); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Type of service = 0x%02x", ip->ip_tos); + (void) snprintf(get_line(0, 0), get_line_remain(), + " xxx. .... = %d (precedence)", + ip->ip_tos >> 5); + (void) snprintf(get_line(0, 0), get_line_remain(), + " %s", getflag(ip->ip_tos, IPTOS_LOWDELAY, + "low delay", "normal delay")); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(ip->ip_tos, IPTOS_THROUGHPUT, + "high throughput", "normal throughput")); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(ip->ip_tos, IPTOS_RELIABILITY, + "high reliability", "normal reliability")); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(ip->ip_tos, IPTOS_ECT, + "ECN capable transport", "not ECN capable transport")); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(ip->ip_tos, IPTOS_CE, + "ECN congestion experienced", + "no ECN congestion experienced")); + /* warning: ip_len is signed in netinet/ip.h */ + uitmp = ntohs(ip->ip_len); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Total length = %u bytes%s", uitmp, + iplen > fraglen ? " -- truncated" : ""); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Identification = %d", ntohs(ip->ip_id)); + /* warning: ip_off is signed in netinet/ip.h */ + uitmp = ntohs(ip->ip_off); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Flags = 0x%x", uitmp >> 12); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(uitmp >> 8, IP_DF >> 8, + "do not fragment", "may fragment")); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(uitmp >> 8, IP_MF >> 8, + "more fragments", "last fragment")); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Fragment offset = %u bytes", + fragoffset); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Time to live = %d seconds/hops", + ip->ip_ttl); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Protocol = %d (%s)", ip->ip_p, + getproto(ip->ip_p)); + /* + * XXX need to compute checksum and print whether it's correct + */ + (void) snprintf(get_line(0, 0), get_line_remain(), + "Header checksum = %04x", + ntohs(ip->ip_sum)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Source address = %s, %s", + inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Destination address = %s, %s", + inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst)); + + /* Print IP options - if any */ + + print_ipoptions((const uchar_t *)(ip + 1), + hdrlen - sizeof (struct ip)); + show_space(); + } + + /* + * If we are in detail mode, and this is not the first fragment of + * a fragmented packet, print out a little line stating this. + * Otherwise, go to the next protocol layer only if this is not a + * fragment, or we are in detail mode and this is the first fragment + * of a fragmented packet. + */ + if (flags & F_DTAIL && fragoffset != 0) { + (void) snprintf(get_detail_line(0, 0), MAXLINE, + "%s: [%d byte(s) of data, continuation of IP ident=%d]", + getproto(ip->ip_p), + iplen, + ntohs(ip->ip_id)); + } else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) { + /* go to the next protocol layer */ + + if (fraglen > 0) { + switch (ip->ip_p) { + case IPPROTO_IP: + break; + case IPPROTO_ENCAP: + (void) interpret_ip(flags, + /* LINTED: alignment */ + (const struct ip *)data, fraglen); + break; + case IPPROTO_ICMP: + (void) interpret_icmp(flags, + /* LINTED: alignment */ + (struct icmp *)data, iplen, fraglen); + break; + case IPPROTO_IGMP: + interpret_igmp(flags, data, iplen, fraglen); + break; + case IPPROTO_GGP: + break; + case IPPROTO_TCP: + (void) interpret_tcp(flags, + (struct tcphdr *)data, iplen, fraglen); + break; + + case IPPROTO_ESP: + (void) interpret_esp(flags, data, iplen, + fraglen); + break; + case IPPROTO_AH: + (void) interpret_ah(flags, data, iplen, + fraglen); + break; + + case IPPROTO_OSPF: + interpret_ospf(flags, data, iplen, fraglen); + break; + + case IPPROTO_EGP: + case IPPROTO_PUP: + break; + case IPPROTO_UDP: + (void) interpret_udp(flags, + (struct udphdr *)data, iplen, fraglen); + break; + + case IPPROTO_IDP: + case IPPROTO_HELLO: + case IPPROTO_ND: + case IPPROTO_RAW: + break; + case IPPROTO_IPV6: /* IPV6 encap */ + /* LINTED: alignment */ + (void) interpret_ipv6(flags, (ip6_t *)data, + iplen); + break; + case IPPROTO_SCTP: + (void) interpret_sctp(flags, + (struct sctp_hdr *)data, iplen, fraglen); + break; + } + } + } + + encap_levels--; + return (iplen); +} + +int +interpret_ipv6(int flags, const ip6_t *ip6h, int fraglen) +{ + uint8_t *data; + int hdrlen, iplen; + int version, flow, class; + uchar_t proto; + boolean_t isfrag = B_FALSE; + uint8_t extmask; + /* + * The print_srcname and print_dstname strings are the hostname + * parts of the verbose IPv6 header output, including the comma + * and the space after the litteral address strings. + */ + char print_srcname[MAXHOSTNAMELEN + 2]; + char print_dstname[MAXHOSTNAMELEN + 2]; + char src_addrstr[INET6_ADDRSTRLEN]; + char dst_addrstr[INET6_ADDRSTRLEN]; + + iplen = ntohs(ip6h->ip6_plen); + hdrlen = IPV6_HDR_LEN; + fraglen -= hdrlen; + if (fraglen < 0) + return (fraglen + hdrlen); + data = ((uint8_t *)ip6h) + hdrlen; + + proto = ip6h->ip6_nxt; + + src_name = addrtoname(AF_INET6, &ip6h->ip6_src); + dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst); + + /* + * Use endian-aware masks to extract traffic class and + * flowinfo. Also, flowinfo is now 20 bits and class 8 + * rather than 24 and 4. + */ + class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20); + flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL); + + /* + * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive, + * so the code within the first part of the following if statement + * will not affect the detailed printing of the packet. + */ + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "IPv6 S=%s D=%s LEN=%d HOPS=%d CLASS=0x%x FLOW=0x%x", + src_name, dst_name, iplen, ip6h->ip6_hops, class, flow); + } else if (flags & F_DTAIL) { + + (void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr, + INET6_ADDRSTRLEN); + (void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr, + INET6_ADDRSTRLEN); + + version = ntohl(ip6h->ip6_vcf) >> 28; + + if (strcmp(src_name, src_addrstr) == 0) { + print_srcname[0] = '\0'; + } else { + snprintf(print_srcname, sizeof (print_srcname), + ", %s", src_name); + } + + if (strcmp(dst_name, dst_addrstr) == 0) { + print_dstname[0] = '\0'; + } else { + snprintf(print_dstname, sizeof (print_dstname), + ", %s", dst_name); + } + + show_header("IPv6: ", "IPv6 Header", iplen); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Version = %d", version); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Traffic Class = %d", class); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Flow label = 0x%x", flow); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Payload length = %d", iplen); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Next Header = %d (%s)", proto, + getproto(proto)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hop Limit = %d", ip6h->ip6_hops); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Source address = %s%s", src_addrstr, print_srcname); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Destination address = %s%s", dst_addrstr, print_dstname); + + show_space(); + } + + /* + * Print IPv6 Extension Headers, or skip them in the summary case. + * Set isfrag to true if one of the extension headers encounterred + * was a fragment header. + */ + if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS || + proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) { + extmask = print_ipv6_extensions(flags, &data, &proto, &iplen, + &fraglen); + if ((extmask & SNOOP_FRAGMENT) != 0) { + isfrag = B_TRUE; + } + } + + /* + * We only want to print upper layer information if this is not + * a fragment, or if we're printing in detail. Note that the + * proto variable will be set to IPPROTO_NONE if this is a fragment + * with a non-zero fragment offset. + */ + if (!isfrag || flags & F_DTAIL) { + /* go to the next protocol layer */ + + switch (proto) { + case IPPROTO_IP: + break; + case IPPROTO_ENCAP: + /* LINTED: alignment */ + (void) interpret_ip(flags, (const struct ip *)data, + fraglen); + break; + case IPPROTO_ICMPV6: + /* LINTED: alignment */ + (void) interpret_icmpv6(flags, (icmp6_t *)data, iplen, + fraglen); + break; + case IPPROTO_IGMP: + interpret_igmp(flags, data, iplen, fraglen); + break; + case IPPROTO_GGP: + break; + case IPPROTO_TCP: + (void) interpret_tcp(flags, (struct tcphdr *)data, + iplen, fraglen); + break; + case IPPROTO_ESP: + (void) interpret_esp(flags, data, iplen, fraglen); + break; + case IPPROTO_AH: + (void) interpret_ah(flags, data, iplen, fraglen); + break; + case IPPROTO_EGP: + case IPPROTO_PUP: + break; + case IPPROTO_UDP: + (void) interpret_udp(flags, (struct udphdr *)data, + iplen, fraglen); + break; + case IPPROTO_IDP: + case IPPROTO_HELLO: + case IPPROTO_ND: + case IPPROTO_RAW: + break; + case IPPROTO_IPV6: + /* LINTED: alignment */ + (void) interpret_ipv6(flags, (const ip6_t *)data, + iplen); + break; + case IPPROTO_SCTP: + (void) interpret_sctp(flags, (struct sctp_hdr *)data, + iplen, fraglen); + break; + case IPPROTO_OSPF: + interpret_ospf6(flags, data, iplen, fraglen); + break; + } + } + + return (iplen); +} + +/* + * ip_ext: data including the extension header. + * iplen: length of the data remaining in the packet. + * Returns a mask of IPv6 extension headers it processed. + */ +uint8_t +print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen, + int *fraglen) +{ + uint8_t *data_ptr; + uchar_t proto = *next; + boolean_t is_extension_header; + struct ip6_hbh *ipv6ext_hbh; + struct ip6_dest *ipv6ext_dest; + struct ip6_rthdr *ipv6ext_rthdr; + struct ip6_frag *ipv6ext_frag; + uint32_t exthdrlen; + uint8_t extmask = 0; + + if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0)) + return (0); + + data_ptr = *hdr; + is_extension_header = B_TRUE; + while (is_extension_header) { + + /* + * There must be at least enough data left to read the + * next header and header length fields from the next + * header. + */ + if (*fraglen < 2) { + return (extmask); + } + + switch (proto) { + case IPPROTO_HOPOPTS: + ipv6ext_hbh = (struct ip6_hbh *)data_ptr; + exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8; + if (*fraglen <= exthdrlen) { + return (extmask); + } + prt_hbh_options(flags, ipv6ext_hbh); + extmask |= SNOOP_HOPOPTS; + proto = ipv6ext_hbh->ip6h_nxt; + break; + case IPPROTO_DSTOPTS: + ipv6ext_dest = (struct ip6_dest *)data_ptr; + exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8; + if (*fraglen <= exthdrlen) { + return (extmask); + } + prt_dest_options(flags, ipv6ext_dest); + extmask |= SNOOP_DSTOPTS; + proto = ipv6ext_dest->ip6d_nxt; + break; + case IPPROTO_ROUTING: + ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr; + exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8; + if (*fraglen <= exthdrlen) { + return (extmask); + } + prt_routing_hdr(flags, ipv6ext_rthdr); + extmask |= SNOOP_ROUTING; + proto = ipv6ext_rthdr->ip6r_nxt; + break; + case IPPROTO_FRAGMENT: + /* LINTED: alignment */ + ipv6ext_frag = (struct ip6_frag *)data_ptr; + exthdrlen = sizeof (struct ip6_frag); + if (*fraglen <= exthdrlen) { + return (extmask); + } + prt_fragment_hdr(flags, ipv6ext_frag); + extmask |= SNOOP_FRAGMENT; + /* + * If this is not the first fragment, forget about + * the rest of the packet, snoop decoding is + * stateless. + */ + if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0) + proto = IPPROTO_NONE; + else + proto = ipv6ext_frag->ip6f_nxt; + break; + default: + is_extension_header = B_FALSE; + break; + } + + if (is_extension_header) { + *iplen -= exthdrlen; + *fraglen -= exthdrlen; + data_ptr += exthdrlen; + } + } + + *next = proto; + *hdr = data_ptr; + return (extmask); +} + +static void +print_ipoptions(const uchar_t *opt, int optlen) +{ + int len; + int remain; + char *line; + const char *truncstr; + + if (optlen <= 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "No options"); + return; + } + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Options: (%d bytes)", optlen); + + while (optlen > 0) { + line = get_line(0, 0); + remain = get_line_remain(); + len = opt[1]; + truncstr = len > optlen ? "?" : ""; + switch (opt[0]) { + case IPOPT_EOL: + (void) strlcpy(line, " - End of option list", remain); + return; + case IPOPT_NOP: + (void) strlcpy(line, " - No op", remain); + len = 1; + break; + case IPOPT_RR: + (void) snprintf(line, remain, + " - Record route (%d bytes%s)", len, truncstr); + print_route(opt); + break; + case IPOPT_TS: + (void) snprintf(line, remain, + " - Time stamp (%d bytes%s)", len, truncstr); + break; + case IPOPT_SECURITY: + (void) snprintf(line, remain, " - RIPSO (%d bytes%s)", + len, truncstr); + print_ripso(opt); + break; + case IPOPT_COMSEC: + (void) snprintf(line, remain, " - CIPSO (%d bytes%s)", + len, truncstr); + print_cipso(opt); + break; + case IPOPT_LSRR: + (void) snprintf(line, remain, + " - Loose source route (%d bytes%s)", len, + truncstr); + print_route(opt); + break; + case IPOPT_SATID: + (void) snprintf(line, remain, + " - SATNET Stream id (%d bytes%s)", + len, truncstr); + break; + case IPOPT_SSRR: + (void) snprintf(line, remain, + " - Strict source route, (%d bytes%s)", len, + truncstr); + print_route(opt); + break; + default: + (void) snprintf(line, remain, + " - Option %d (unknown - %d bytes%s) %s", + opt[0], len, truncstr, + tohex((char *)&opt[2], len - 2)); + break; + } + if (len <= 0) { + (void) snprintf(line, remain, + " - Incomplete option len %d", len); + break; + } + opt += len; + optlen -= len; + } +} + +static void +print_route(const uchar_t *opt) +{ + int len, pointer, remain; + struct in_addr addr; + char *line; + + len = opt[1]; + pointer = opt[2]; + + (void) snprintf(get_line(0, 0), get_line_remain(), + " Pointer = %d", pointer); + + pointer -= IPOPT_MINOFF; + opt += (IPOPT_OFFSET + 1); + len -= (IPOPT_OFFSET + 1); + + while (len > 0) { + line = get_line(0, 0); + remain = get_line_remain(); + memcpy((char *)&addr, opt, sizeof (addr)); + if (addr.s_addr == INADDR_ANY) + (void) strlcpy(line, " -", remain); + else + (void) snprintf(line, remain, " %s", + addrtoname(AF_INET, &addr)); + if (pointer == 0) + (void) strlcat(line, " <-- (current)", remain); + + opt += sizeof (addr); + len -= sizeof (addr); + pointer -= sizeof (addr); + } +} + +char * +getproto(int p) +{ + switch (p) { + case IPPROTO_HOPOPTS: return ("IPv6-HopOpts"); + case IPPROTO_IPV6: return ("IPv6"); + case IPPROTO_ROUTING: return ("IPv6-Route"); + case IPPROTO_FRAGMENT: return ("IPv6-Frag"); + case IPPROTO_RSVP: return ("RSVP"); + case IPPROTO_ENCAP: return ("IP-in-IP"); + case IPPROTO_AH: return ("AH"); + case IPPROTO_ESP: return ("ESP"); + case IPPROTO_ICMP: return ("ICMP"); + case IPPROTO_ICMPV6: return ("ICMPv6"); + case IPPROTO_DSTOPTS: return ("IPv6-DstOpts"); + case IPPROTO_IGMP: return ("IGMP"); + case IPPROTO_GGP: return ("GGP"); + case IPPROTO_TCP: return ("TCP"); + case IPPROTO_EGP: return ("EGP"); + case IPPROTO_PUP: return ("PUP"); + case IPPROTO_UDP: return ("UDP"); + case IPPROTO_IDP: return ("IDP"); + case IPPROTO_HELLO: return ("HELLO"); + case IPPROTO_ND: return ("ND"); + case IPPROTO_EON: return ("EON"); + case IPPROTO_RAW: return ("RAW"); + case IPPROTO_OSPF: return ("OSPF"); + default: return (""); + } +} + +static void +prt_routing_hdr(int flags, const struct ip6_rthdr *ipv6ext_rthdr) +{ + uint8_t nxt_hdr; + uint8_t type; + uint32_t len; + uint8_t segleft; + uint32_t numaddrs; + int i; + struct ip6_rthdr0 *ipv6ext_rthdr0; + struct in6_addr *addrs; + char addr[INET6_ADDRSTRLEN]; + + /* in summary mode, we don't do anything. */ + if (flags & F_SUM) { + return; + } + + nxt_hdr = ipv6ext_rthdr->ip6r_nxt; + type = ipv6ext_rthdr->ip6r_type; + len = 8 * (ipv6ext_rthdr->ip6r_len + 1); + segleft = ipv6ext_rthdr->ip6r_segleft; + + show_header("IPv6-Route: ", "IPv6 Routing Header", 0); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Header length = %d", len); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Routing type = %d", type); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Segments left = %d", segleft); + + if (type == IPV6_RTHDR_TYPE_0) { + /* + * XXX This loop will print all addresses in the routing header, + * XXX not just the segments left. + * XXX (The header length field is twice the number of + * XXX addresses) + * XXX At some future time, we may want to change this + * XXX to differentiate between the hops yet to do + * XXX and the hops already taken. + */ + /* LINTED: alignment */ + ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr; + numaddrs = ipv6ext_rthdr0->ip6r0_len / 2; + addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1); + for (i = 0; i < numaddrs; i++) { + (void) inet_ntop(AF_INET6, &addrs[i], addr, + INET6_ADDRSTRLEN); + (void) snprintf(get_line(0, 0), get_line_remain(), + "address[%d]=%s", i, addr); + } + } + + show_space(); +} + +static void +prt_fragment_hdr(int flags, const struct ip6_frag *ipv6ext_frag) +{ + boolean_t morefrag; + uint16_t fragoffset; + uint8_t nxt_hdr; + uint32_t fragident; + + /* extract the various fields from the fragment header */ + nxt_hdr = ipv6ext_frag->ip6f_nxt; + morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0 + ? B_FALSE : B_TRUE; + fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK); + fragident = ntohl(ipv6ext_frag->ip6f_ident); + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "IPv6 fragment ID=%u Offset=%-4d MF=%d", + fragident, + fragoffset, + morefrag); + } else { /* F_DTAIL */ + show_header("IPv6-Frag: ", "IPv6 Fragment Header", 0); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Fragment Offset = %d", fragoffset); + (void) snprintf(get_line(0, 0), get_line_remain(), + "More Fragments Flag = %s", morefrag ? "true" : "false"); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Identification = %u", fragident); + + show_space(); + } +} + +static void +print_ip6opt_ls(const uchar_t *data, unsigned int op_len) +{ + uint32_t doi; + uint8_t sotype, solen; + uint16_t value, value2; + char *cp; + int remlen; + boolean_t printed; + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Labeled Security Option len = %u bytes%s", op_len, + op_len < sizeof (uint32_t) || (op_len & 1) != 0 ? "?" : ""); + if (op_len < sizeof (uint32_t)) + return; + GETINT32(doi, data); + (void) snprintf(get_line(0, 0), get_line_remain(), + " DOI = %d (%s)", doi, doi == IP6LS_DOI_V4 ? "IPv4" : "???"); + op_len -= sizeof (uint32_t); + while (op_len > 0) { + GETINT8(sotype, data); + if (op_len < 2) { + (void) snprintf(get_line(0, 0), get_line_remain(), + " truncated %u suboption (no len)", sotype); + break; + } + GETINT8(solen, data); + if (solen < 2 || solen > op_len) { + (void) snprintf(get_line(0, 0), get_line_remain(), + " bad %u suboption (len 2 <= %u <= %u)", + sotype, solen, op_len); + if (solen < 2) + solen = 2; + if (solen > op_len) + solen = op_len; + } + op_len -= solen; + solen -= 2; + cp = get_line(0, 0); + remlen = get_line_remain(); + (void) strlcpy(cp, " ", remlen); + cp += 4; + remlen -= 4; + printed = B_TRUE; + switch (sotype) { + case IP6LS_TT_LEVEL: + if (solen != 2) { + printed = B_FALSE; + break; + } + GETINT16(value, data); + (void) snprintf(cp, remlen, "Level %u", value); + solen = 0; + break; + case IP6LS_TT_VECTOR: + (void) strlcpy(cp, "Bit-Vector: ", remlen); + remlen -= strlen(cp); + cp += strlen(cp); + while (solen > 1) { + GETINT16(value, data); + solen -= 2; + (void) snprintf(cp, remlen, "%04x", value); + remlen -= strlen(cp); + cp += strlen(cp); + } + break; + case IP6LS_TT_ENUM: + (void) strlcpy(cp, "Enumeration:", remlen); + remlen -= strlen(cp); + cp += strlen(cp); + while (solen > 1) { + GETINT16(value, data); + solen -= 2; + (void) snprintf(cp, remlen, " %u", value); + remlen -= strlen(cp); + cp += strlen(cp); + } + break; + case IP6LS_TT_RANGES: + (void) strlcpy(cp, "Ranges:", remlen); + remlen -= strlen(cp); + cp += strlen(cp); + while (solen > 3) { + GETINT16(value, data); + GETINT16(value2, data); + solen -= 4; + (void) snprintf(cp, remlen, " %u-%u", value, + value2); + remlen -= strlen(cp); + cp += strlen(cp); + } + break; + case IP6LS_TT_V4: + (void) strlcpy(cp, "IPv4 Option", remlen); + print_ipoptions(data, solen); + solen = 0; + break; + case IP6LS_TT_DEST: + (void) snprintf(cp, remlen, + "Destination-Only Data length %u", solen); + solen = 0; + break; + default: + (void) snprintf(cp, remlen, + " unknown %u suboption (len %u)", sotype, solen); + solen = 0; + break; + } + if (solen != 0) { + if (printed) { + cp = get_line(0, 0); + remlen = get_line_remain(); + } + (void) snprintf(cp, remlen, + " malformed %u suboption (remaining %u)", + sotype, solen); + data += solen; + } + } +} + +static void +prt_hbh_options(int flags, const struct ip6_hbh *ipv6ext_hbh) +{ + const uint8_t *data, *ndata; + uint32_t len; + uint8_t op_type; + uint8_t op_len; + uint8_t nxt_hdr; + + /* in summary mode, we don't do anything. */ + if (flags & F_SUM) { + return; + } + + show_header("IPv6-HopOpts: ", "IPv6 Hop-by-Hop Options Header", 0); + show_space(); + + /* + * Store the lengh of this ext hdr in bytes. The caller has + * ensured that there is at least len bytes of data left. + */ + len = ipv6ext_hbh->ip6h_len * 8 + 8; + + ndata = (const uint8_t *)ipv6ext_hbh + 2; + len -= 2; + + nxt_hdr = ipv6ext_hbh->ip6h_nxt; + (void) snprintf(get_line(0, 0), get_line_remain(), + "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr)); + + while (len > 0) { + data = ndata; + GETINT8(op_type, data); + /* This is the only one-octet IPv6 option */ + if (op_type == IP6OPT_PAD1) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "pad1 option "); + len--; + ndata = data; + continue; + } + GETINT8(op_len, data); + if (len < 2 || op_len + 2 > len) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Error: option %u truncated (%u + 2 > %u)", + op_type, op_len, len); + op_len = len - 2; + /* + * Continue processing the malformed option so that we + * can display as much as possible. + */ + } + + /* advance pointers to the next option */ + len -= op_len + 2; + ndata = data + op_len; + + /* process this option */ + switch (op_type) { + case IP6OPT_PADN: + (void) snprintf(get_line(0, 0), get_line_remain(), + "padN option len = %u", op_len); + break; + case IP6OPT_JUMBO: { + uint32_t payload_len; + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Jumbo Payload Option len = %u bytes%s", op_len, + op_len == sizeof (uint32_t) ? "" : "?"); + if (op_len == sizeof (uint32_t)) { + GETINT32(payload_len, data); + (void) snprintf(get_line(0, 0), + get_line_remain(), + "Jumbo Payload Length = %u bytes", + payload_len); + } + break; + } + case IP6OPT_ROUTER_ALERT: { + uint16_t value; + const char *label[] = {"MLD", "RSVP", "AN"}; + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Router Alert Option len = %u bytes%s", op_len, + op_len == sizeof (uint16_t) ? "" : "?"); + if (op_len == sizeof (uint16_t)) { + GETINT16(value, data); + (void) snprintf(get_line(0, 0), + get_line_remain(), + "Alert Type = %d (%s)", value, + value < sizeof (label) / sizeof (label[0]) ? + label[value] : "???"); + } + break; + } + case IP6OPT_LS: + print_ip6opt_ls(data, op_len); + break; + default: + (void) snprintf(get_line(0, 0), get_line_remain(), + "Option type = %u, len = %u", op_type, op_len); + break; + } + } + + show_space(); +} + +static void +prt_dest_options(int flags, const struct ip6_dest *ipv6ext_dest) +{ + const uint8_t *data, *ndata; + uint32_t len; + uint8_t op_type; + uint32_t op_len; + uint8_t nxt_hdr; + uint8_t value; + + /* in summary mode, we don't do anything. */ + if (flags & F_SUM) { + return; + } + + show_header("IPv6-DstOpts: ", "IPv6 Destination Options Header", 0); + show_space(); + + /* + * Store the length of this ext hdr in bytes. The caller has + * ensured that there is at least len bytes of data left. + */ + len = ipv6ext_dest->ip6d_len * 8 + 8; + + ndata = (const uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */ + len -= 2; + + nxt_hdr = ipv6ext_dest->ip6d_nxt; + (void) snprintf(get_line(0, 0), get_line_remain(), + "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr)); + + while (len > 0) { + data = ndata; + GETINT8(op_type, data); + if (op_type == IP6OPT_PAD1) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "pad1 option "); + len--; + ndata = data; + continue; + } + GETINT8(op_len, data); + if (len < 2 || op_len + 2 > len) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Error: option %u truncated (%u + 2 > %u)", + op_type, op_len, len); + op_len = len - 2; + /* + * Continue processing the malformed option so that we + * can display as much as possible. + */ + } + + /* advance pointers to the next option */ + len -= op_len + 2; + ndata = data + op_len; + + /* process this option */ + switch (op_type) { + case IP6OPT_PADN: + (void) snprintf(get_line(0, 0), get_line_remain(), + "padN option len = %u", op_len); + break; + case IP6OPT_TUNNEL_LIMIT: + GETINT8(value, data); + (void) snprintf(get_line(0, 0), get_line_remain(), + "tunnel encapsulation limit len = %d, value = %d", + op_len, value); + break; + case IP6OPT_LS: + print_ip6opt_ls(data, op_len); + break; + default: + (void) snprintf(get_line(0, 0), get_line_remain(), + "Option type = %u, len = %u", op_type, op_len); + break; + } + } + + show_space(); +} + +#define ALABEL_MAXLEN 256 + +static char ascii_label[ALABEL_MAXLEN]; +static char *plabel = ascii_label; + +struct snoop_pair { + int val; + const char *name; +}; + +static struct snoop_pair ripso_class_tbl[] = { + TSOL_CL_TOP_SECRET, "TOP SECRET", + TSOL_CL_SECRET, "SECRET", + TSOL_CL_CONFIDENTIAL, "CONFIDENTIAL", + TSOL_CL_UNCLASSIFIED, "UNCLASSIFIED", + -1, NULL +}; + +static struct snoop_pair ripso_prot_tbl[] = { + TSOL_PA_GENSER, "GENSER", + TSOL_PA_SIOP_ESI, "SIOP-ESI", + TSOL_PA_SCI, "SCI", + TSOL_PA_NSA, "NSA", + TSOL_PA_DOE, "DOE", + 0x04, "UNASSIGNED", + 0x02, "UNASSIGNED", + -1, NULL +}; + +static struct snoop_pair * +get_pair_byval(struct snoop_pair pairlist[], int val) +{ + int i; + + for (i = 0; pairlist[i].name != NULL; i++) + if (pairlist[i].val == val) + return (&pairlist[i]); + return (NULL); +} + +static void +print_ripso(const uchar_t *opt) +{ + struct snoop_pair *ripso_class; + int i, index, prot_len; + boolean_t first_prot; + char line[100], *ptr; + + prot_len = opt[1] - 3; + if (prot_len < 0) + return; + + show_header("RIPSO: ", "Revised IP Security Option", 0); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Type = Basic Security Option (%d), Length = %d", opt[0], opt[1]); + + /* + * Display Classification Level + */ + ripso_class = get_pair_byval(ripso_class_tbl, (int)opt[2]); + if (ripso_class == NULL) + (void) snprintf(get_line(0, 0), get_line_remain(), + "Classification = Unknown (0x%02x)", opt[2]); + else + (void) snprintf(get_line(0, 0), get_line_remain(), + "Classification = %s (0x%02x)", + ripso_class->name, ripso_class->val); + + /* + * Display Protection Authority Flags + */ + (void) snprintf(line, sizeof (line), "Protection Authority = "); + ptr = line; + first_prot = B_TRUE; + for (i = 0; i < prot_len; i++) { + index = 0; + while (ripso_prot_tbl[index].name != NULL) { + if (opt[3 + i] & ripso_prot_tbl[index].val) { + ptr = strchr(ptr, 0); + if (!first_prot) { + (void) strlcpy(ptr, ", ", + sizeof (line) - (ptr - line)); + ptr = strchr(ptr, 0); + } + (void) snprintf(ptr, + sizeof (line) - (ptr - line), + "%s (0x%02x)", + ripso_prot_tbl[index].name, + ripso_prot_tbl[index].val); + } + index++; + } + if ((opt[3 + i] & 1) == 0) + break; + } + if (!first_prot) + (void) snprintf(get_line(0, 0), get_line_remain(), "%s", line); + else + (void) snprintf(get_line(0, 0), get_line_remain(), "%sNone", + line); +} + +#define CIPSO_GENERIC_ARRAY_LEN 200 + +/* + * Return 1 if CIPSO SL and Categories are all 1's; 0 otherwise. + * + * Note: opt starts with "Tag Type": + * + * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)| + * + */ +static boolean_t +cipso_high(const uchar_t *opt) +{ + int i; + + if (((int)opt[1] + 6) < IP_MAX_OPT_LENGTH) + return (B_FALSE); + for (i = 0; i < ((int)opt[1] - 3); i++) + if (opt[3 + i] != 0xff) + return (B_FALSE); + return (B_TRUE); +} + +/* + * Converts CIPSO label to SL. + * + * Note: opt starts with "Tag Type": + * + * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)| + * + */ +static void +cipso2sl(const uchar_t *opt, bslabel_t *sl, int *high) +{ + int i, taglen; + uchar_t *q = (uchar_t *)&((_bslabel_impl_t *)sl)->compartments; + + *high = 0; + taglen = opt[1]; + memset((caddr_t)sl, 0, sizeof (bslabel_t)); + + if (cipso_high(opt)) { + BSLHIGH(sl); + *high = 1; + } else { + LCLASS_SET((_bslabel_impl_t *)sl, opt[3]); + for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) + q[i] = opt[TSOL_TT1_MIN_LENGTH + i]; + } + SETBLTYPE(sl, SUN_SL_ID); +} + +static int +interpret_cipso_tagtype1(const uchar_t *opt) +{ + int i, taglen, ishigh; + bslabel_t sl; + char line[CIPSO_GENERIC_ARRAY_LEN], *ptr; + + taglen = opt[1]; + if (taglen < TSOL_TT1_MIN_LENGTH || + taglen > TSOL_TT1_MAX_LENGTH) + return (taglen); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Tag Type = %d, Tag Length = %d", opt[0], opt[1]); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Sensitivity Level = 0x%02x", opt[3]); + ptr = line; + for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) { + (void) snprintf(ptr, sizeof (line) - (ptr - line), "%02x", + opt[TSOL_TT1_MIN_LENGTH + i]); + ptr = strchr(ptr, 0); + } + if (i != 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Categories = "); + (void) snprintf(get_line(0, 0), get_line_remain(), "\t%s", + line); + } else { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Categories = None"); + } + cipso2sl(opt, &sl, &ishigh); + if (is_system_labeled()) { + if (bsltos(&sl, &plabel, ALABEL_MAXLEN, + LONG_CLASSIFICATION|LONG_WORDS|VIEW_INTERNAL) < 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "The Sensitivity Level and Categories can't be " + "mapped to a valid SL"); + } else { + (void) snprintf(get_line(0, 0), get_line_remain(), + "The Sensitivity Level and Categories are mapped " + "to the SL:"); + (void) snprintf(get_line(0, 0), get_line_remain(), + "\t%s", ascii_label); + } + } + return (taglen); +} + +/* + * The following struct definition #define's are copied from TS1.x. They are + * not used here (except TTYPE_3_MAX_TOKENS), but included as a reference for + * the tag type 3 packet format. + */ +#define TTYPE_3_MAX_TOKENS 7 + +/* + * Display CIPSO tag type 3 which is defined by MAXSIX. + */ +static int +interpret_cipso_tagtype3(const uchar_t *opt) +{ + uchar_t tagtype; + int index, numtokens, taglen; + uint16_t mask; + uint32_t token; + static const char *name[] = { + "SL", + "NCAV", + "INTEG", + "SID", + "undefined", + "undefined", + "IL", + "PRIVS", + "LUID", + "PID", + "IDS", + "ACL" + }; + + tagtype = *opt++; + (void) memcpy(&mask, opt + 3, sizeof (mask)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Tag Type = %d (MAXSIX)", tagtype); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Generation = 0x%02x%02x%02x, Mask = 0x%04x", opt[0], opt[1], + opt[2], mask); + opt += 3 + sizeof (mask); + + /* + * Display tokens + */ + numtokens = 0; + index = 0; + while (mask != 0 && numtokens < TTYPE_3_MAX_TOKENS) { + if (mask & 0x0001) { + (void) memcpy(&token, opt, sizeof (token)); + opt += sizeof (token); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Attribute = %s, Token = 0x%08x", + index < sizeof (name) / sizeof (*name) ? + name[index] : "unknown", token); + numtokens++; + } + mask = mask >> 1; + index++; + } + + taglen = 6 + numtokens * 4; + return (taglen); +} + +static void +print_cipso(const uchar_t *opt) +{ + int optlen, taglen, tagnum; + uint32_t doi; + char line[CIPSO_GENERIC_ARRAY_LEN]; + char *oldnest; + + optlen = opt[1]; + if (optlen < TSOL_CIPSO_MIN_LENGTH || optlen > TSOL_CIPSO_MAX_LENGTH) + return; + + oldnest = prot_nest_prefix; + prot_nest_prefix = prot_prefix; + show_header("CIPSO: ", "Common IP Security Option", 0); + show_space(); + + /* + * Display CIPSO Header + */ + (void) snprintf(get_line(0, 0), get_line_remain(), + "Type = CIPSO (%d), Length = %d", opt[0], opt[1]); + (void) memcpy(&doi, opt + 2, sizeof (doi)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Domain of Interpretation = %u", (unsigned)ntohl(doi)); + + if (opt[1] == TSOL_CIPSO_MIN_LENGTH) { /* no tags */ + show_space(); + prot_prefix = prot_nest_prefix; + prot_nest_prefix = oldnest; + return; + } + optlen -= TSOL_CIPSO_MIN_LENGTH; + opt += TSOL_CIPSO_MIN_LENGTH; + + /* + * Display Each Tag + */ + tagnum = 1; + while (optlen >= TSOL_TT1_MIN_LENGTH) { + (void) snprintf(line, sizeof (line), "Tag# %d", tagnum); + show_header("CIPSO: ", line, 0); + /* + * We handle tag type 1 and 3 only. Note, tag type 3 + * is MAXSIX defined. + */ + switch (opt[0]) { + case 1: + taglen = interpret_cipso_tagtype1(opt); + break; + case 3: + taglen = interpret_cipso_tagtype3(opt); + break; + default: + (void) snprintf(get_line(0, 0), get_line_remain(), + "Unknown Tag Type %d", opt[0]); + show_space(); + prot_prefix = prot_nest_prefix; + prot_nest_prefix = oldnest; + return; + } + + /* + * Move to the next tag + */ + if (taglen <= 0) + break; + optlen -= taglen; + opt += taglen; + tagnum++; + } + show_space(); + prot_prefix = prot_nest_prefix; + prot_nest_prefix = oldnest; +} |