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
interpret_tcp(int flags,struct tcphdr * tcp,int iplen,int fraglen)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
print_tcpoptions(opt,optlen)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
print_tcpoptions_summary(uchar_t * opt,int optlen,char * line)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