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