Home | History | Annotate | Download | only in snoop
      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, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <fcntl.h>
     33 #include <string.h>
     34 #include <sys/types.h>
     35 #include <sys/time.h>
     36 
     37 #include <sys/socket.h>
     38 #include <net/if.h>
     39 #include <netinet/in_systm.h>
     40 #include <netinet/in.h>
     41 #include <netinet/ip.h>
     42 #include <netinet/if_ether.h>
     43 #include <netinet/tcp.h>
     44 #include "snoop.h"
     45 
     46 extern char *dlc_header;
     47 
     48 #define	TCPOPT_HEADER_LEN	2
     49 #define	TCPOPT_TSTAMP_LEN	10
     50 #define	TCPOPT_SACK_LEN		8
     51 
     52 /*
     53  * Convert a network byte order 32 bit integer to a host order integer.
     54  * ntohl() cannot be used because option values may not be aligned properly.
     55  */
     56 #define	GET_UINT32(opt)	(((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
     57 	((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
     58 	((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
     59 	((uint_t)*((uchar_t *)(opt) + 3)))
     60 
     61 static void print_tcpoptions_summary(uchar_t *, int, char *);
     62 static void print_tcpoptions(uchar_t *, int);
     63 
     64 static const struct {
     65 	unsigned int	tf_flag;
     66 	const char	*tf_name;
     67 } tcp_flags[] = {
     68 	{ TH_SYN, 	"Syn"	},
     69 	{ TH_FIN, 	"Fin"	},
     70 	{ TH_RST, 	"Rst"	},
     71 	{ TH_PUSH,	"Push"	},
     72 	{ TH_ECE,	"ECE"	},
     73 	{ TH_CWR,	"CWR"	},
     74 	{ 0,		NULL	}
     75 };
     76 
     77 int
     78 interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
     79 {
     80 	char *data;
     81 	int hdrlen, tcplen;
     82 	int sunrpc = 0;
     83 	char *pname;
     84 	char buff[32];
     85 	char *line, *endline;
     86 	unsigned int i;
     87 
     88 	hdrlen = tcp->th_off * 4;
     89 	data = (char *)tcp + hdrlen;
     90 	tcplen = iplen - hdrlen;
     91 	fraglen -= hdrlen;
     92 	if (fraglen < 0)
     93 		return (fraglen + hdrlen);	/* incomplete header */
     94 	if (fraglen > tcplen)
     95 		fraglen = tcplen;
     96 
     97 	if (flags & F_SUM) {
     98 		line = get_sum_line();
     99 		endline = line + MAXLINE;
    100 		(void) snprintf(line, endline - line, "TCP D=%d S=%d",
    101 		    ntohs(tcp->th_dport), ntohs(tcp->th_sport));
    102 		line += strlen(line);
    103 
    104 		for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
    105 			if (tcp->th_flags & tcp_flags[i].tf_flag) {
    106 				(void) snprintf(line, endline - line, " %s",
    107 				    tcp_flags[i].tf_name);
    108 				line += strlen(line);
    109 			}
    110 		}
    111 
    112 		if (tcp->th_flags & TH_URG) {
    113 			(void) snprintf(line, endline - line, " Urg=%u",
    114 			    ntohs(tcp->th_urp));
    115 			line += strlen(line);
    116 		}
    117 		if (tcp->th_flags & TH_ACK) {
    118 			(void) snprintf(line, endline - line, " Ack=%u",
    119 				ntohl(tcp->th_ack));
    120 			line += strlen(line);
    121 		}
    122 		if (ntohl(tcp->th_seq)) {
    123 			(void) snprintf(line, endline - line, " Seq=%u Len=%d",
    124 				ntohl(tcp->th_seq), tcplen);
    125 			line += strlen(line);
    126 		}
    127 		(void) snprintf(line, endline - line, " Win=%d",
    128 		    ntohs(tcp->th_win));
    129 		print_tcpoptions_summary((uchar_t *)(tcp + 1),
    130 		    (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
    131 	}
    132 
    133 	sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
    134 		!reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
    135 		valid_rpc(data + 4, fraglen - 4);
    136 
    137 	if (flags & F_DTAIL) {
    138 
    139 	show_header("TCP:  ", "TCP Header", tcplen);
    140 	show_space();
    141 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport -
    142 		dlc_header, 2),	"Source port = %d", ntohs(tcp->th_sport));
    143 
    144 	if (sunrpc) {
    145 		pname = "(Sun RPC)";
    146 	} else {
    147 		pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
    148 		if (pname == NULL) {
    149 			pname = "";
    150 		} else {
    151 			(void) sprintf(buff, "(%s)", pname);
    152 			pname = buff;
    153 		}
    154 	}
    155 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport -
    156 		dlc_header, 2), "Destination port = %d %s",
    157 		ntohs(tcp->th_dport), pname);
    158 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq -
    159 		dlc_header, 4),	"Sequence number = %u",
    160 		ntohl(tcp->th_seq));
    161 	(void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4),
    162 		"Acknowledgement number = %u",
    163 		ntohl(tcp->th_ack));
    164 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) +
    165 		4, 1), "Data offset = %d bytes", tcp->th_off * 4);
    166 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    167 		dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags);
    168 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    169 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_CWR,
    170 		"ECN congestion window reduced",
    171 		"No ECN congestion window reduced"));
    172 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    173 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ECE,
    174 		"ECN echo", "No ECN echo"));
    175 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    176 		dlc_header) + 4, 1), "      %s",
    177 		getflag(tcp->th_flags, TH_URG,
    178 		"Urgent pointer", "No urgent pointer"));
    179 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    180 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_ACK,
    181 		"Acknowledgement", "No acknowledgement"));
    182 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    183 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_PUSH,
    184 		"Push", "No push"));
    185 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    186 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_RST,
    187 		"Reset", "No reset"));
    188 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    189 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_SYN,
    190 		"Syn", "No Syn"));
    191 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
    192 		dlc_header) + 4, 1), "      %s", getflag(tcp->th_flags, TH_FIN,
    193 		"Fin", "No Fin"));
    194 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) +
    195 		4, 1), "Window = %d", ntohs(tcp->th_win));
    196 	/* XXX need to compute checksum and print whether correct */
    197 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) +
    198 		4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum));
    199 	(void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) +
    200 		4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp));
    201 
    202 	/* Print TCP options - if any */
    203 
    204 	print_tcpoptions((uchar_t *)(tcp + 1),
    205 	    tcp->th_off * 4 - sizeof (struct tcphdr));
    206 
    207 	show_space();
    208 	}
    209 
    210 	/* go to the next protocol layer */
    211 
    212 	if (!interpret_reserved(flags, IPPROTO_TCP,
    213 		ntohs(tcp->th_sport),
    214 		ntohs(tcp->th_dport),
    215 		data, fraglen)) {
    216 		if (sunrpc && fraglen > 0)
    217 			interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
    218 	}
    219 
    220 	return (tcplen);
    221 }
    222 
    223 static void
    224 print_tcpoptions(opt, optlen)
    225 	uchar_t *opt;
    226 	int optlen;
    227 {
    228 	int	 len;
    229 	char	 *line;
    230 	uchar_t	*sack_opt;
    231 	uchar_t	*end_opt;
    232 	int	sack_len;
    233 
    234 	if (optlen <= 0) {
    235 		(void) sprintf(get_line((char *)&opt - dlc_header, 1),
    236 		"No options");
    237 		return;
    238 	}
    239 
    240 	(void) sprintf(get_line((char *)&opt - dlc_header, 1),
    241 	"Options: (%d bytes)", optlen);
    242 
    243 	while (optlen > 0) {
    244 		line = get_line((char *)&opt - dlc_header, 1);
    245 		len = opt[1];
    246 		switch (opt[0]) {
    247 		case TCPOPT_EOL:
    248 			(void) strcpy(line, "  - End of option list");
    249 			return;
    250 		case TCPOPT_NOP:
    251 			(void) strcpy(line, "  - No operation");
    252 			len = 1;
    253 			break;
    254 		case TCPOPT_MAXSEG:
    255 			(void) sprintf(line,
    256 			"  - Maximum segment size = %d bytes",
    257 				(opt[2] << 8) + opt[3]);
    258 			break;
    259 		case TCPOPT_WSCALE:
    260 			(void) sprintf(line, "  - Window scale = %d", opt[2]);
    261 			break;
    262 		case TCPOPT_TSTAMP:
    263 			/* Sanity check. */
    264 			if (optlen < TCPOPT_TSTAMP_LEN) {
    265 				(void) sprintf(line,
    266 				    "  - Incomplete TS option");
    267 			} else {
    268 				(void) sprintf(line,
    269 				    "  - TS Val = %u, TS Echo = %u",
    270 				    GET_UINT32(opt + 2),
    271 				    GET_UINT32(opt + 6));
    272 			}
    273 			break;
    274 		case TCPOPT_SACK_PERMITTED:
    275 			(void) sprintf(line, "  - SACK permitted option");
    276 			break;
    277 		case TCPOPT_SACK:
    278 			/*
    279 			 * Sanity check.  Total length should be greater
    280 			 * than just the option header length.
    281 			 */
    282 			if (len <= TCPOPT_HEADER_LEN ||
    283 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
    284 				(void) sprintf(line,
    285 				    "  - Incomplete SACK option");
    286 				break;
    287 			}
    288 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
    289 			sack_opt = opt + TCPOPT_HEADER_LEN;
    290 			end_opt = opt + optlen;
    291 
    292 			(void) sprintf(line, "  - SACK blocks:");
    293 			line = get_line((char *)&opt - dlc_header, 1);
    294 			(void) sprintf(line, "        ");
    295 			while (sack_len > 0) {
    296 				char sack_blk[MAXLINE + 1];
    297 
    298 				/*
    299 				 * sack_len may not tell us the truth about
    300 				 * the real length...  Need to be careful
    301 				 * not to step beyond the option buffer.
    302 				 */
    303 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
    304 					(void) strcat(line,
    305 					    "...incomplete SACK block");
    306 					break;
    307 				}
    308 				(void) sprintf(sack_blk, "(%u-%u) ",
    309 				    GET_UINT32(sack_opt),
    310 				    GET_UINT32(sack_opt + 4));
    311 				(void) strcat(line, sack_blk);
    312 				sack_opt += TCPOPT_SACK_LEN;
    313 				sack_len -= TCPOPT_SACK_LEN;
    314 			}
    315 			break;
    316 		default:
    317 			(void) sprintf(line,
    318 			"  - Option %d (unknown - %d bytes) %s",
    319 				opt[0],
    320 				len - 2,
    321 				tohex((char *)&opt[2], len - 2));
    322 			break;
    323 		}
    324 		if (len <= 0) {
    325 			(void) sprintf(line, "  - Incomplete option len %d",
    326 				len);
    327 			break;
    328 		}
    329 		opt += len;
    330 		optlen -= len;
    331 	}
    332 }
    333 
    334 /*
    335  * This function is basically the same as print_tcpoptions() except that
    336  * all options are printed on the same line.
    337  */
    338 static void
    339 print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
    340 {
    341 	int	 len;
    342 	uchar_t	*sack_opt;
    343 	uchar_t	*end_opt;
    344 	int	sack_len;
    345 	char	options[MAXLINE + 1];
    346 
    347 	if (optlen <= 0) {
    348 		return;
    349 	}
    350 
    351 	(void) strcat(line, " Options=<");
    352 	while (optlen > 0) {
    353 		len = opt[1];
    354 		switch (opt[0]) {
    355 		case TCPOPT_EOL:
    356 			(void) strcat(line, "eol>");
    357 			return;
    358 		case TCPOPT_NOP:
    359 			(void) strcat(line, "nop");
    360 			len = 1;
    361 			break;
    362 		case TCPOPT_MAXSEG:
    363 			(void) sprintf(options, "mss %d",
    364 			    (opt[2] << 8) + opt[3]);
    365 			(void) strcat(line, options);
    366 			break;
    367 		case TCPOPT_WSCALE:
    368 			(void) sprintf(options, "wscale %d", opt[2]);
    369 			(void) strcat(line, options);
    370 			break;
    371 		case TCPOPT_TSTAMP:
    372 			/* Sanity check. */
    373 			if (optlen < TCPOPT_TSTAMP_LEN) {
    374 				(void) strcat(line, "tstamp|");
    375 			} else {
    376 				(void) sprintf(options,
    377 				    "tstamp %u %u", GET_UINT32(opt + 2),
    378 				    GET_UINT32(opt + 6));
    379 				(void) strcat(line, options);
    380 			}
    381 			break;
    382 		case TCPOPT_SACK_PERMITTED:
    383 			(void) strcat(line, "sackOK");
    384 			break;
    385 		case TCPOPT_SACK:
    386 			/*
    387 			 * Sanity check.  Total length should be greater
    388 			 * than just the option header length.
    389 			 */
    390 			if (len <= TCPOPT_HEADER_LEN ||
    391 			    opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
    392 				(void) strcat(line, "sack|");
    393 				break;
    394 			}
    395 			sack_len = opt[1] - TCPOPT_HEADER_LEN;
    396 			sack_opt = opt + TCPOPT_HEADER_LEN;
    397 			end_opt = opt + optlen;
    398 
    399 			(void) strcat(line, "sack");
    400 			while (sack_len > 0) {
    401 				/*
    402 				 * sack_len may not tell us the truth about
    403 				 * the real length...  Need to be careful
    404 				 * not to step beyond the option buffer.
    405 				 */
    406 				if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
    407 					(void) strcat(line, "|");
    408 					break;
    409 				}
    410 				(void) sprintf(options, " %u-%u",
    411 				    GET_UINT32(sack_opt),
    412 				    GET_UINT32(sack_opt + 4));
    413 				(void) strcat(line, options);
    414 				sack_opt += TCPOPT_SACK_LEN;
    415 				sack_len -= TCPOPT_SACK_LEN;
    416 			}
    417 			break;
    418 		default:
    419 			(void) sprintf(options, "unknown %d", opt[0]);
    420 			(void) strcat(line, options);
    421 			break;
    422 		}
    423 		if (len <= 0) {
    424 			(void) sprintf(options, "optlen %d", len);
    425 			(void) strcat(line, options);
    426 			break;
    427 		}
    428 		opt += len;
    429 		optlen -= len;
    430 		if (optlen > 0) {
    431 			(void) strcat(line, ",");
    432 		}
    433 	}
    434 	(void) strcat(line, ">");
    435 }
    436