]> The Tcpdump Group git mirrors - tcpdump/blob - print-tcp.c
support print the NFS-over-TCP
[tcpdump] / print-tcp.c
1 /*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 #ifndef lint
23 static const char rcsid[] =
24 "@(#) $Header: /tcpdump/master/tcpdump/print-tcp.c,v 1.66 2000-06-01 01:09:32 assar Exp $ (LBL)";
25 #endif
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <sys/param.h>
32 #include <sys/time.h>
33
34 #include <rpc/rpc.h>
35
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/ip.h>
39 #include <netinet/ip_var.h>
40 #include <netinet/tcp.h>
41
42 #ifdef HAVE_MEMORY_H
43 #include <memory.h>
44 #endif
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #ifdef INET6
51 #include <netinet/ip6.h>
52 #endif
53
54 #include "interface.h"
55 #include "addrtoname.h"
56 #include "extract.h"
57
58 /* Compatibility */
59 #ifndef TCPOPT_WSCALE
60 #define TCPOPT_WSCALE 3 /* window scale factor (rfc1072) */
61 #endif
62 #ifndef TCPOPT_SACKOK
63 #define TCPOPT_SACKOK 4 /* selective ack ok (rfc1072) */
64 #endif
65 #ifndef TCPOPT_SACK
66 #define TCPOPT_SACK 5 /* selective ack (rfc1072) */
67 #endif
68 #ifndef TCPOPT_ECHO
69 #define TCPOPT_ECHO 6 /* echo (rfc1072) */
70 #endif
71 #ifndef TCPOPT_ECHOREPLY
72 #define TCPOPT_ECHOREPLY 7 /* echo (rfc1072) */
73 #endif
74 #ifndef TCPOPT_TIMESTAMP
75 #define TCPOPT_TIMESTAMP 8 /* timestamps (rfc1323) */
76 #endif
77 #ifndef TCPOPT_CC
78 #define TCPOPT_CC 11 /* T/TCP CC options (rfc1644) */
79 #endif
80 #ifndef TCPOPT_CCNEW
81 #define TCPOPT_CCNEW 12 /* T/TCP CC options (rfc1644) */
82 #endif
83 #ifndef TCPOPT_CCECHO
84 #define TCPOPT_CCECHO 13 /* T/TCP CC options (rfc1644) */
85 #endif
86
87 /*
88 * Definitions required for ECN
89 * for use if the OS running tcpdump does not have ECN
90 */
91 #ifndef TH_ECNECHO
92 #define TH_ECNECHO 0x40 /* ECN Echo in tcp header */
93 #endif
94 #ifndef TH_CWR
95 #define TH_CWR 0x80 /* ECN Cwnd Reduced in tcp header*/
96 #endif
97
98 struct tha {
99 #ifndef INET6
100 struct in_addr src;
101 struct in_addr dst;
102 #else
103 struct in6_addr src;
104 struct in6_addr dst;
105 #endif /*INET6*/
106 u_int port;
107 };
108
109 struct tcp_seq_hash {
110 struct tcp_seq_hash *nxt;
111 struct tha addr;
112 tcp_seq seq;
113 tcp_seq ack;
114 };
115
116 #define TSEQ_HASHSIZE 919
117
118 /* These tcp optinos do not have the size octet */
119 #define ZEROLENOPT(o) ((o) == TCPOPT_EOL || (o) == TCPOPT_NOP)
120
121 static struct tcp_seq_hash tcp_seq_hash[TSEQ_HASHSIZE];
122
123
124 #ifndef TELNET_PORT
125 #define TELNET_PORT 23
126 #endif
127 #ifndef BGP_PORT
128 #define BGP_PORT 179
129 #endif
130 #define NETBIOS_SSN_PORT 139
131 #ifndef NFS_PORT
132 #define NFS_PORT 2049
133 #endif
134
135 void
136 tcp_print(register const u_char *bp, register u_int length,
137 register const u_char *bp2)
138 {
139 register const struct tcphdr *tp;
140 register const struct ip *ip;
141 register u_char flags;
142 register int hlen;
143 register char ch;
144 u_int16_t sport, dport, win, urp;
145 u_int32_t seq, ack, thseq, thack;
146 int threv;
147 #ifdef INET6
148 register const struct ip6_hdr *ip6;
149 #endif
150
151 tp = (struct tcphdr *)bp;
152 ip = (struct ip *)bp2;
153 #ifdef INET6
154 if (ip->ip_v == 6)
155 ip6 = (struct ip6_hdr *)bp2;
156 else
157 ip6 = NULL;
158 #endif /*INET6*/
159 ch = '\0';
160 if (!TTEST(tp->th_dport)) {
161 (void)printf("%s > %s: [|tcp]",
162 ipaddr_string(&ip->ip_src),
163 ipaddr_string(&ip->ip_dst));
164 return;
165 }
166
167 sport = ntohs(tp->th_sport);
168 dport = ntohs(tp->th_dport);
169
170
171 hlen = tp->th_off * 4;
172
173 /*
174 * If data present and NFS port used, assume NFS.
175 * Pass offset of data plus 4 bytes for RPC TCP msg length
176 * to NFS print routines.
177 */
178 if (!qflag) {
179 if ((u_char *)tp + 4 + sizeof(struct rpc_msg) <= snapend &&
180 dport == NFS_PORT) {
181 nfsreq_print((u_char *)tp + hlen + 4, length-hlen,
182 (u_char *)ip);
183 return;
184 } else if ((u_char *)tp + 4 + sizeof(struct rpc_msg)
185 <= snapend &&
186 sport == NFS_PORT) {
187 nfsreply_print((u_char *)tp + hlen + 4,length-hlen,
188 (u_char *)ip);
189 return;
190 }
191 }
192 #ifdef INET6
193 if (ip6) {
194 if (ip6->ip6_nxt == IPPROTO_TCP) {
195 (void)printf("%s.%s > %s.%s: ",
196 ip6addr_string(&ip6->ip6_src),
197 tcpport_string(sport),
198 ip6addr_string(&ip6->ip6_dst),
199 tcpport_string(dport));
200 } else {
201 (void)printf("%s > %s: ",
202 tcpport_string(sport), tcpport_string(dport));
203 }
204 } else
205 #endif /*INET6*/
206 {
207 if (ip->ip_p == IPPROTO_TCP) {
208 (void)printf("%s.%s > %s.%s: ",
209 ipaddr_string(&ip->ip_src),
210 tcpport_string(sport),
211 ipaddr_string(&ip->ip_dst),
212 tcpport_string(dport));
213 } else {
214 (void)printf("%s > %s: ",
215 tcpport_string(sport), tcpport_string(dport));
216 }
217 }
218
219 TCHECK(*tp);
220
221 seq = (u_int32_t)ntohl(tp->th_seq);
222 ack = (u_int32_t)ntohl(tp->th_ack);
223 win = ntohs(tp->th_win);
224 urp = ntohs(tp->th_urp);
225
226 if (qflag) {
227 (void)printf("tcp %d", length - tp->th_off * 4);
228 return;
229 }
230 if ((flags = tp->th_flags) & (TH_SYN|TH_FIN|TH_RST|TH_PUSH|
231 TH_ECNECHO|TH_CWR)) {
232 if (flags & TH_SYN)
233 putchar('S');
234 if (flags & TH_FIN)
235 putchar('F');
236 if (flags & TH_RST)
237 putchar('R');
238 if (flags & TH_PUSH)
239 putchar('P');
240 if (flags & TH_CWR)
241 putchar('W'); /* congestion _W_indow reduced (ECN) */
242 if (flags & TH_ECNECHO)
243 putchar('E'); /* ecn _E_cho sent (ECN) */
244 } else
245 putchar('.');
246
247 if (!Sflag && (flags & TH_ACK)) {
248 register struct tcp_seq_hash *th;
249 register int rev;
250 struct tha tha;
251 /*
252 * Find (or record) the initial sequence numbers for
253 * this conversation. (we pick an arbitrary
254 * collating order so there's only one entry for
255 * both directions).
256 */
257 #ifdef INET6
258 bzero(&tha, sizeof(tha));
259 rev = 0;
260 if (ip6) {
261 if (sport > dport) {
262 rev = 1;
263 } else if (sport == dport) {
264 int i;
265
266 for (i = 0; i < 4; i++) {
267 if (((u_int32_t *)(&ip6->ip6_src))[i] >
268 ((u_int32_t *)(&ip6->ip6_dst))[i]) {
269 rev = 1;
270 break;
271 }
272 }
273 }
274 if (rev) {
275 tha.src = ip6->ip6_dst;
276 tha.dst = ip6->ip6_src;
277 tha.port = dport << 16 | sport;
278 } else {
279 tha.dst = ip6->ip6_dst;
280 tha.src = ip6->ip6_src;
281 tha.port = sport << 16 | dport;
282 }
283 } else {
284 if (sport > dport ||
285 (sport == dport &&
286 ip->ip_src.s_addr > ip->ip_dst.s_addr)) {
287 rev = 1;
288 }
289 if (rev) {
290 *(struct in_addr *)&tha.src = ip->ip_dst;
291 *(struct in_addr *)&tha.dst = ip->ip_src;
292 tha.port = dport << 16 | sport;
293 } else {
294 *(struct in_addr *)&tha.dst = ip->ip_dst;
295 *(struct in_addr *)&tha.src = ip->ip_src;
296 tha.port = sport << 16 | dport;
297 }
298 }
299 #else
300 if (sport < dport ||
301 (sport == dport &&
302 ip->ip_src.s_addr < ip->ip_dst.s_addr)) {
303 tha.src = ip->ip_src, tha.dst = ip->ip_dst;
304 tha.port = sport << 16 | dport;
305 rev = 0;
306 } else {
307 tha.src = ip->ip_dst, tha.dst = ip->ip_src;
308 tha.port = dport << 16 | sport;
309 rev = 1;
310 }
311 #endif
312
313 threv = rev;
314 for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
315 th->nxt; th = th->nxt)
316 if (!memcmp((char *)&tha, (char *)&th->addr,
317 sizeof(th->addr)))
318 break;
319
320 if (!th->nxt || (flags & TH_SYN)) {
321 /* didn't find it or new conversation */
322 if (th->nxt == NULL) {
323 th->nxt = (struct tcp_seq_hash *)
324 calloc(1, sizeof(*th));
325 if (th->nxt == NULL)
326 error("tcp_print: calloc");
327 }
328 th->addr = tha;
329 if (rev)
330 th->ack = seq, th->seq = ack - 1;
331 else
332 th->seq = seq, th->ack = ack - 1;
333
334 } else {
335 if (rev)
336 seq -= th->ack, ack -= th->seq;
337 else
338 seq -= th->seq, ack -= th->ack;
339 }
340
341 thseq = th->seq;
342 thack = th->ack;
343 } else {
344 /*fool gcc*/
345 thseq = thack = threv = 0;
346 }
347 if (hlen > length) {
348 (void)printf(" [bad hdr length]");
349 return;
350 }
351 length -= hlen;
352 if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST))
353 (void)printf(" %u:%u(%d)", seq, seq + length, length);
354 if (flags & TH_ACK)
355 (void)printf(" ack %u", ack);
356
357 (void)printf(" win %d", win);
358
359 if (flags & TH_URG)
360 (void)printf(" urg %d", urp);
361 /*
362 * Handle any options.
363 */
364 if ((hlen -= sizeof(*tp)) > 0) {
365 register const u_char *cp;
366 register int i, opt, len, datalen;
367
368 cp = (const u_char *)tp + sizeof(*tp);
369 putchar(' ');
370 ch = '<';
371 while (hlen > 0) {
372 putchar(ch);
373 TCHECK(*cp);
374 opt = *cp++;
375 if (ZEROLENOPT(opt))
376 len = 1;
377 else {
378 TCHECK(*cp);
379 len = *cp++; /* total including type, len */
380 if (len < 2 || len > hlen)
381 goto bad;
382 --hlen; /* account for length byte */
383 }
384 --hlen; /* account for type byte */
385 datalen = 0;
386
387 /* Bail if "l" bytes of data are not left or were not captured */
388 #define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); }
389
390 switch (opt) {
391
392 case TCPOPT_MAXSEG:
393 (void)printf("mss");
394 datalen = 2;
395 LENCHECK(datalen);
396 (void)printf(" %u", EXTRACT_16BITS(cp));
397
398 break;
399
400 case TCPOPT_EOL:
401 (void)printf("eol");
402 break;
403
404 case TCPOPT_NOP:
405 (void)printf("nop");
406 break;
407
408 case TCPOPT_WSCALE:
409 (void)printf("wscale");
410 datalen = 1;
411 LENCHECK(datalen);
412 (void)printf(" %u", *cp);
413 break;
414
415 case TCPOPT_SACKOK:
416 (void)printf("sackOK");
417 break;
418
419 case TCPOPT_SACK:
420 (void)printf("sack");
421 datalen = len - 2;
422 if (datalen % 8 != 0) {
423 (void)printf(" malformed sack ");
424 } else {
425 u_int32_t s, e;
426
427 (void)printf(" sack %d ", datalen / 8);
428 for (i = 0; i < datalen; i += 8) {
429 LENCHECK(i + 4);
430 s = EXTRACT_32BITS(cp + i);
431 LENCHECK(i + 8);
432 e = EXTRACT_32BITS(cp + i + 4);
433 if (threv) {
434 s -= thseq;
435 e -= thseq;
436 } else {
437 s -= thack;
438 e -= thack;
439 }
440 (void)printf("{%u:%u}", s, e);
441 }
442 (void)printf(" ");
443 }
444 break;
445
446 case TCPOPT_ECHO:
447 (void)printf("echo");
448 datalen = 4;
449 LENCHECK(datalen);
450 (void)printf(" %u", EXTRACT_32BITS(cp));
451 break;
452
453 case TCPOPT_ECHOREPLY:
454 (void)printf("echoreply");
455 datalen = 4;
456 LENCHECK(datalen);
457 (void)printf(" %u", EXTRACT_32BITS(cp));
458 break;
459
460 case TCPOPT_TIMESTAMP:
461 (void)printf("timestamp");
462 datalen = 8;
463 LENCHECK(4);
464 (void)printf(" %u", EXTRACT_32BITS(cp));
465 LENCHECK(datalen);
466 (void)printf(" %u", EXTRACT_32BITS(cp + 4));
467 break;
468
469 case TCPOPT_CC:
470 (void)printf("cc");
471 datalen = 4;
472 LENCHECK(datalen);
473 (void)printf(" %u", EXTRACT_32BITS(cp));
474 break;
475
476 case TCPOPT_CCNEW:
477 (void)printf("ccnew");
478 datalen = 4;
479 LENCHECK(datalen);
480 (void)printf(" %u", EXTRACT_32BITS(cp));
481 break;
482
483 case TCPOPT_CCECHO:
484 (void)printf("ccecho");
485 datalen = 4;
486 LENCHECK(datalen);
487 (void)printf(" %u", EXTRACT_32BITS(cp));
488 break;
489
490 default:
491 (void)printf("opt-%d:", opt);
492 datalen = len - 2;
493 for (i = 0; i < datalen; ++i) {
494 LENCHECK(i);
495 (void)printf("%02x", cp[i]);
496 }
497 break;
498 }
499
500 /* Account for data printed */
501 cp += datalen;
502 hlen -= datalen;
503
504 /* Check specification against observed length */
505 ++datalen; /* option octet */
506 if (!ZEROLENOPT(opt))
507 ++datalen; /* size octet */
508 if (datalen != len)
509 (void)printf("[len %d]", len);
510 ch = ',';
511 if (opt == TCPOPT_EOL)
512 break;
513 }
514 putchar('>');
515 }
516
517 if (length <= 0)
518 return;
519
520 /*
521 * Decode payload if necessary.
522 */
523 bp += (tp->th_off * 4);
524 if (!qflag && vflag && length > 0
525 && (sport == TELNET_PORT || dport == TELNET_PORT))
526 telnet_print(bp, length);
527 else if (sport == BGP_PORT || dport == BGP_PORT)
528 bgp_print(bp, length);
529 else if (sport == NETBIOS_SSN_PORT || dport == NETBIOS_SSN_PORT)
530 nbt_tcp_print(bp, length);
531 return;
532 bad:
533 fputs("[bad opt]", stdout);
534 if (ch != '\0')
535 putchar('>');
536 return;
537 trunc:
538 fputs("[|tcp]", stdout);
539 if (ch != '\0')
540 putchar('>');
541 }
542