1190207Srpaulo/*	$NetBSD: print-tcp.c,v 1.9 2007/07/26 18:15:12 plunky Exp $	*/
2190207Srpaulo
317680Spst/*
439297Sfenner * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
517680Spst *	The Regents of the University of California.  All rights reserved.
617680Spst *
7146773Ssam * Copyright (c) 1999-2004 The tcpdump.org project
8146773Ssam *
917680Spst * Redistribution and use in source and binary forms, with or without
1017680Spst * modification, are permitted provided that: (1) source code distributions
1117680Spst * retain the above copyright notice and this paragraph in its entirety, (2)
1217680Spst * distributions including binary code include the above copyright notice and
1317680Spst * this paragraph in its entirety in the documentation or other materials
1417680Spst * provided with the distribution, and (3) all advertising materials mentioning
1517680Spst * features or use of this software display the following acknowledgement:
1617680Spst * ``This product includes software developed by the University of California,
1717680Spst * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
1817680Spst * the University nor the names of its contributors may be used to endorse
1917680Spst * or promote products derived from this software without specific prior
2017680Spst * written permission.
2117680Spst * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
2217680Spst * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
2317680Spst * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2417680Spst */
2517680Spst
2617680Spst#ifndef lint
27127668Sbmsstatic const char rcsid[] _U_ =
28214478Srpaulo"@(#) $Header: /tcpdump/master/tcpdump/print-tcp.c,v 1.135 2008-11-09 23:35:03 mcr Exp $ (LBL)";
29214478Srpaulo#else
30190207Srpaulo__RCSID("$NetBSD: print-tcp.c,v 1.8 2007/07/24 11:53:48 drochner Exp $");
3117680Spst#endif
3217680Spst
3356893Sfenner#ifdef HAVE_CONFIG_H
3456893Sfenner#include "config.h"
3556893Sfenner#endif
3656893Sfenner
37127668Sbms#include <tcpdump-stdinc.h>
3817680Spst
3917680Spst#include <stdio.h>
4017680Spst#include <stdlib.h>
4117680Spst#include <string.h>
4217680Spst
4317680Spst#include "interface.h"
4417680Spst#include "addrtoname.h"
4517680Spst#include "extract.h"
4617680Spst
4775115Sfenner#include "tcp.h"
4875115Sfenner
4975115Sfenner#include "ip.h"
5075115Sfenner#ifdef INET6
5175115Sfenner#include "ip6.h"
5275115Sfenner#endif
53127668Sbms#include "ipproto.h"
54146773Ssam#include "rpc_auth.h"
55146773Ssam#include "rpc_msg.h"
5675115Sfenner
5798524Sfenner#include "nameser.h"
5898524Sfenner
59146773Ssam#ifdef HAVE_LIBCRYPTO
60146773Ssam#include <openssl/md5.h>
61214478Srpaulo#include <signature.h>
62146773Ssam
63146773Ssamstatic int tcp_verify_signature(const struct ip *ip, const struct tcphdr *tp,
64190207Srpaulo                                const u_char *data, int length, const u_char *rcvsig);
65146773Ssam#endif
66146773Ssam
6775115Sfennerstatic void print_tcp_rst_data(register const u_char *sp, u_int length);
6875115Sfenner
6975115Sfenner#define MAX_RST_DATA_LEN	30
7075115Sfenner
7117680Spst
7217680Spststruct tha {
7356893Sfenner#ifndef INET6
74190207Srpaulo        struct in_addr src;
75190207Srpaulo        struct in_addr dst;
7656893Sfenner#else
77190207Srpaulo        struct in6_addr src;
78190207Srpaulo        struct in6_addr dst;
7956893Sfenner#endif /*INET6*/
80190207Srpaulo        u_int port;
8117680Spst};
8217680Spst
8317680Spststruct tcp_seq_hash {
84190207Srpaulo        struct tcp_seq_hash *nxt;
85190207Srpaulo        struct tha addr;
86190207Srpaulo        tcp_seq seq;
87190207Srpaulo        tcp_seq ack;
8817680Spst};
8917680Spst
9017680Spst#define TSEQ_HASHSIZE 919
9117680Spst
9217680Spst/* These tcp optinos do not have the size octet */
9317680Spst#define ZEROLENOPT(o) ((o) == TCPOPT_EOL || (o) == TCPOPT_NOP)
9417680Spst
9517680Spststatic struct tcp_seq_hash tcp_seq_hash[TSEQ_HASHSIZE];
9617680Spst
97190207Srpaulostruct tok tcp_flag_values[] = {
98190207Srpaulo        { TH_FIN, "F" },
99190207Srpaulo        { TH_SYN, "S" },
100190207Srpaulo        { TH_RST, "R" },
101190207Srpaulo        { TH_PUSH, "P" },
102190207Srpaulo        { TH_ACK, "." },
103190207Srpaulo        { TH_URG, "U" },
104190207Srpaulo        { TH_ECNECHO, "E" },
105190207Srpaulo        { TH_CWR, "W" },
106190207Srpaulo        { 0, NULL }
107190207Srpaulo};
10817680Spst
109190207Srpaulostruct tok tcp_option_values[] = {
110190207Srpaulo        { TCPOPT_EOL, "eol" },
111190207Srpaulo        { TCPOPT_NOP, "nop" },
112190207Srpaulo        { TCPOPT_MAXSEG, "mss" },
113190207Srpaulo        { TCPOPT_WSCALE, "wscale" },
114190207Srpaulo        { TCPOPT_SACKOK, "sackOK" },
115190207Srpaulo        { TCPOPT_SACK, "sack" },
116190207Srpaulo        { TCPOPT_ECHO, "echo" },
117190207Srpaulo        { TCPOPT_ECHOREPLY, "echoreply" },
118190207Srpaulo        { TCPOPT_TIMESTAMP, "TS" },
119190207Srpaulo        { TCPOPT_CC, "cc" },
120190207Srpaulo        { TCPOPT_CCNEW, "ccnew" },
121190207Srpaulo        { TCPOPT_CCECHO, "" },
122190207Srpaulo        { TCPOPT_SIGNATURE, "md5" },
123190207Srpaulo        { TCPOPT_AUTH, "enhanced auth" },
124197829Srpaulo        { TCPOPT_UTO, "uto" },
125190207Srpaulo        { 0, NULL }
126190207Srpaulo};
12756893Sfenner
12875115Sfennerstatic int tcp_cksum(register const struct ip *ip,
12975115Sfenner		     register const struct tcphdr *tp,
130127668Sbms		     register u_int len)
13175115Sfenner{
132236192Sdelphij	return (nextproto4_cksum(ip, (const u_int8_t *)tp, len,
133236192Sdelphij	    IPPROTO_TCP));
13475115Sfenner}
13575115Sfenner
13617680Spstvoid
13717680Spsttcp_print(register const u_char *bp, register u_int length,
13875115Sfenner	  register const u_char *bp2, int fragmented)
13917680Spst{
140190207Srpaulo        register const struct tcphdr *tp;
141190207Srpaulo        register const struct ip *ip;
142190207Srpaulo        register u_char flags;
143190207Srpaulo        register u_int hlen;
144190207Srpaulo        register char ch;
145190207Srpaulo        u_int16_t sport, dport, win, urp;
146190207Srpaulo        u_int32_t seq, ack, thseq, thack;
147214478Srpaulo        u_int utoval;
148190207Srpaulo        int threv;
14956893Sfenner#ifdef INET6
150190207Srpaulo        register const struct ip6_hdr *ip6;
15156893Sfenner#endif
15217680Spst
153190207Srpaulo        tp = (struct tcphdr *)bp;
154190207Srpaulo        ip = (struct ip *)bp2;
15556893Sfenner#ifdef INET6
156190207Srpaulo        if (IP_V(ip) == 6)
157190207Srpaulo                ip6 = (struct ip6_hdr *)bp2;
158190207Srpaulo        else
159190207Srpaulo                ip6 = NULL;
16056893Sfenner#endif /*INET6*/
161190207Srpaulo        ch = '\0';
162190207Srpaulo        if (!TTEST(tp->th_dport)) {
163190207Srpaulo                (void)printf("%s > %s: [|tcp]",
164190207Srpaulo                             ipaddr_string(&ip->ip_src),
165190207Srpaulo                             ipaddr_string(&ip->ip_dst));
166190207Srpaulo                return;
167190207Srpaulo        }
16817680Spst
169190207Srpaulo        sport = EXTRACT_16BITS(&tp->th_sport);
170190207Srpaulo        dport = EXTRACT_16BITS(&tp->th_dport);
17156893Sfenner
172190207Srpaulo        hlen = TH_OFF(tp) * 4;
17375115Sfenner
174190207Srpaulo        /*
175146773Ssam	 * If data present, header length valid, and NFS port used,
176146773Ssam	 * assume NFS.
17775115Sfenner	 * Pass offset of data plus 4 bytes for RPC TCP msg length
17875115Sfenner	 * to NFS print routines.
17975115Sfenner	 */
180190207Srpaulo	if (!qflag && hlen >= sizeof(*tp) && hlen <= length &&
181190207Srpaulo	    (length - hlen) >= 4) {
182190207Srpaulo		u_char *fraglenp;
183190207Srpaulo		u_int32_t fraglen;
184190207Srpaulo		register struct sunrpc_msg *rp;
185190207Srpaulo		enum sunrpc_msg_type direction;
186190207Srpaulo
187190207Srpaulo		fraglenp = (u_char *)tp + hlen;
188190207Srpaulo		if (TTEST2(*fraglenp, 4)) {
189190207Srpaulo			fraglen = EXTRACT_32BITS(fraglenp) & 0x7FFFFFFF;
190190207Srpaulo			if (fraglen > (length - hlen) - 4)
191190207Srpaulo				fraglen = (length - hlen) - 4;
192190207Srpaulo			rp = (struct sunrpc_msg *)(fraglenp + 4);
193190207Srpaulo			if (TTEST(rp->rm_direction)) {
194190207Srpaulo				direction = (enum sunrpc_msg_type)EXTRACT_32BITS(&rp->rm_direction);
195190207Srpaulo				if (dport == NFS_PORT &&
196190207Srpaulo				    direction == SUNRPC_CALL) {
197190207Srpaulo					nfsreq_print((u_char *)rp, fraglen,
198190207Srpaulo					    (u_char *)ip);
199190207Srpaulo					return;
200190207Srpaulo				}
201190207Srpaulo				if (sport == NFS_PORT &&
202190207Srpaulo				    direction == SUNRPC_REPLY) {
203190207Srpaulo					nfsreply_print((u_char *)rp, fraglen,
204190207Srpaulo					    (u_char *)ip);
205190207Srpaulo					return;
206190207Srpaulo				}
207190207Srpaulo			}
208190207Srpaulo                }
209190207Srpaulo        }
21056893Sfenner#ifdef INET6
211190207Srpaulo        if (ip6) {
212190207Srpaulo                if (ip6->ip6_nxt == IPPROTO_TCP) {
213190207Srpaulo                        (void)printf("%s.%s > %s.%s: ",
214190207Srpaulo                                     ip6addr_string(&ip6->ip6_src),
215190207Srpaulo                                     tcpport_string(sport),
216190207Srpaulo                                     ip6addr_string(&ip6->ip6_dst),
217190207Srpaulo                                     tcpport_string(dport));
218190207Srpaulo                } else {
219190207Srpaulo                        (void)printf("%s > %s: ",
220190207Srpaulo                                     tcpport_string(sport), tcpport_string(dport));
221190207Srpaulo                }
222190207Srpaulo        } else
22356893Sfenner#endif /*INET6*/
224190207Srpaulo        {
225190207Srpaulo                if (ip->ip_p == IPPROTO_TCP) {
226190207Srpaulo                        (void)printf("%s.%s > %s.%s: ",
227190207Srpaulo                                     ipaddr_string(&ip->ip_src),
228190207Srpaulo                                     tcpport_string(sport),
229190207Srpaulo                                     ipaddr_string(&ip->ip_dst),
230190207Srpaulo                                     tcpport_string(dport));
231190207Srpaulo                } else {
232190207Srpaulo                        (void)printf("%s > %s: ",
233190207Srpaulo                                     tcpport_string(sport), tcpport_string(dport));
234190207Srpaulo                }
235190207Srpaulo        }
23656893Sfenner
237190207Srpaulo        if (hlen < sizeof(*tp)) {
238190207Srpaulo                (void)printf(" tcp %d [bad hdr length %u - too short, < %lu]",
239190207Srpaulo                             length - hlen, hlen, (unsigned long)sizeof(*tp));
240190207Srpaulo                return;
241190207Srpaulo        }
242146773Ssam
243190207Srpaulo        TCHECK(*tp);
24456893Sfenner
245190207Srpaulo        seq = EXTRACT_32BITS(&tp->th_seq);
246190207Srpaulo        ack = EXTRACT_32BITS(&tp->th_ack);
247190207Srpaulo        win = EXTRACT_16BITS(&tp->th_win);
248190207Srpaulo        urp = EXTRACT_16BITS(&tp->th_urp);
24917680Spst
250190207Srpaulo        if (qflag) {
251190207Srpaulo                (void)printf("tcp %d", length - hlen);
252190207Srpaulo                if (hlen > length) {
253190207Srpaulo                        (void)printf(" [bad hdr length %u - too long, > %u]",
254190207Srpaulo                                     hlen, length);
255190207Srpaulo                }
256190207Srpaulo                return;
257190207Srpaulo        }
25817680Spst
259190207Srpaulo        flags = tp->th_flags;
260190207Srpaulo        printf("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags));
261190207Srpaulo
262190207Srpaulo        if (!Sflag && (flags & TH_ACK)) {
263190207Srpaulo                register struct tcp_seq_hash *th;
264190207Srpaulo                const void *src, *dst;
265190207Srpaulo                register int rev;
266190207Srpaulo                struct tha tha;
267190207Srpaulo                /*
268190207Srpaulo                 * Find (or record) the initial sequence numbers for
269190207Srpaulo                 * this conversation.  (we pick an arbitrary
270190207Srpaulo                 * collating order so there's only one entry for
271190207Srpaulo                 * both directions).
272190207Srpaulo                 */
27356893Sfenner#ifdef INET6
274190207Srpaulo                rev = 0;
275190207Srpaulo                if (ip6) {
276190207Srpaulo                        src = &ip6->ip6_src;
277190207Srpaulo                        dst = &ip6->ip6_dst;
278190207Srpaulo                        if (sport > dport)
279190207Srpaulo                                rev = 1;
280190207Srpaulo                        else if (sport == dport) {
281190207Srpaulo                                if (memcmp(src, dst, sizeof ip6->ip6_dst) > 0)
282190207Srpaulo                                        rev = 1;
283190207Srpaulo                        }
284190207Srpaulo                        if (rev) {
285190207Srpaulo                                memcpy(&tha.src, dst, sizeof ip6->ip6_dst);
286190207Srpaulo                                memcpy(&tha.dst, src, sizeof ip6->ip6_src);
287190207Srpaulo                                tha.port = dport << 16 | sport;
288190207Srpaulo                        } else {
289190207Srpaulo                                memcpy(&tha.dst, dst, sizeof ip6->ip6_dst);
290190207Srpaulo                                memcpy(&tha.src, src, sizeof ip6->ip6_src);
291190207Srpaulo                                tha.port = sport << 16 | dport;
292190207Srpaulo                        }
293190207Srpaulo                } else {
294236192Sdelphij                        /*
295236192Sdelphij                         * Zero out the tha structure; the src and dst
296236192Sdelphij                         * fields are big enough to hold an IPv6
297236192Sdelphij                         * address, but we only have IPv4 addresses
298236192Sdelphij                         * and thus must clear out the remaining 124
299236192Sdelphij                         * bits.
300236192Sdelphij                         *
301236192Sdelphij                         * XXX - should we just clear those bytes after
302236192Sdelphij                         * copying the IPv4 addresses, rather than
303236192Sdelphij                         * zeroing out the entire structure and then
304236192Sdelphij                         * overwriting some of the zeroes?
305236192Sdelphij                         *
306236192Sdelphij                         * XXX - this could fail if we see TCP packets
307236192Sdelphij                         * with an IPv6 address with the lower 124 bits
308236192Sdelphij                         * all zero and also see TCP packes with an
309236192Sdelphij                         * IPv4 address with the same 32 bits as the
310236192Sdelphij                         * upper 32 bits of the IPv6 address in question.
311236192Sdelphij                         * Can that happen?  Is it likely enough to be
312236192Sdelphij                         * an issue?
313236192Sdelphij                         */
314236192Sdelphij                        memset(&tha, 0, sizeof(tha));
315190207Srpaulo                        src = &ip->ip_src;
316190207Srpaulo                        dst = &ip->ip_dst;
317190207Srpaulo                        if (sport > dport)
318190207Srpaulo                                rev = 1;
319190207Srpaulo                        else if (sport == dport) {
320190207Srpaulo                                if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
321190207Srpaulo                                        rev = 1;
322190207Srpaulo                        }
323190207Srpaulo                        if (rev) {
324190207Srpaulo                                memcpy(&tha.src, dst, sizeof ip->ip_dst);
325190207Srpaulo                                memcpy(&tha.dst, src, sizeof ip->ip_src);
326190207Srpaulo                                tha.port = dport << 16 | sport;
327190207Srpaulo                        } else {
328190207Srpaulo                                memcpy(&tha.dst, dst, sizeof ip->ip_dst);
329190207Srpaulo                                memcpy(&tha.src, src, sizeof ip->ip_src);
330190207Srpaulo                                tha.port = sport << 16 | dport;
331190207Srpaulo                        }
332190207Srpaulo                }
33356893Sfenner#else
334190207Srpaulo                rev = 0;
335190207Srpaulo                src = &ip->ip_src;
336190207Srpaulo                dst = &ip->ip_dst;
337190207Srpaulo                if (sport > dport)
338190207Srpaulo                        rev = 1;
339190207Srpaulo                else if (sport == dport) {
340190207Srpaulo                        if (memcmp(src, dst, sizeof ip->ip_dst) > 0)
341190207Srpaulo                                rev = 1;
342190207Srpaulo                }
343190207Srpaulo                if (rev) {
344190207Srpaulo                        memcpy(&tha.src, dst, sizeof ip->ip_dst);
345190207Srpaulo                        memcpy(&tha.dst, src, sizeof ip->ip_src);
346190207Srpaulo                        tha.port = dport << 16 | sport;
347190207Srpaulo                } else {
348190207Srpaulo                        memcpy(&tha.dst, dst, sizeof ip->ip_dst);
349190207Srpaulo                        memcpy(&tha.src, src, sizeof ip->ip_src);
350190207Srpaulo                        tha.port = sport << 16 | dport;
351190207Srpaulo                }
35256893Sfenner#endif
35317680Spst
354190207Srpaulo                threv = rev;
355190207Srpaulo                for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
356190207Srpaulo                     th->nxt; th = th->nxt)
357190207Srpaulo                        if (memcmp((char *)&tha, (char *)&th->addr,
358190207Srpaulo                                   sizeof(th->addr)) == 0)
359190207Srpaulo                                break;
36017680Spst
361190207Srpaulo                if (!th->nxt || (flags & TH_SYN)) {
362190207Srpaulo                        /* didn't find it or new conversation */
363190207Srpaulo                        if (th->nxt == NULL) {
364190207Srpaulo                                th->nxt = (struct tcp_seq_hash *)
365190207Srpaulo                                        calloc(1, sizeof(*th));
366190207Srpaulo                                if (th->nxt == NULL)
367190207Srpaulo                                        error("tcp_print: calloc");
368190207Srpaulo                        }
369190207Srpaulo                        th->addr = tha;
370190207Srpaulo                        if (rev)
371190207Srpaulo                                th->ack = seq, th->seq = ack - 1;
372190207Srpaulo                        else
373190207Srpaulo                                th->seq = seq, th->ack = ack - 1;
374190207Srpaulo                } else {
375190207Srpaulo                        if (rev)
376190207Srpaulo                                seq -= th->ack, ack -= th->seq;
377190207Srpaulo                        else
378190207Srpaulo                                seq -= th->seq, ack -= th->ack;
379190207Srpaulo                }
38075115Sfenner
381190207Srpaulo                thseq = th->seq;
382190207Srpaulo                thack = th->ack;
383190207Srpaulo        } else {
384190207Srpaulo                /*fool gcc*/
385190207Srpaulo                thseq = thack = threv = 0;
386190207Srpaulo        }
387190207Srpaulo        if (hlen > length) {
388190207Srpaulo                (void)printf(" [bad hdr length %u - too long, > %u]",
389190207Srpaulo                             hlen, length);
390190207Srpaulo                return;
391190207Srpaulo        }
39275115Sfenner
393236192Sdelphij        if (vflag && !Kflag && !fragmented) {
394236192Sdelphij                /* Check the checksum, if possible. */
395190207Srpaulo                u_int16_t sum, tcp_sum;
396146773Ssam
397236192Sdelphij                if (IP_V(ip) == 4) {
398236192Sdelphij                        if (TTEST2(tp->th_sport, length)) {
399236192Sdelphij                                sum = tcp_cksum(ip, tp, length);
400190207Srpaulo                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);
401236192Sdelphij
402236192Sdelphij                                (void)printf(", cksum 0x%04x", tcp_sum);
403236192Sdelphij                                if (sum != 0)
404236192Sdelphij                                        (void)printf(" (incorrect -> 0x%04x)",
405236192Sdelphij                                            in_cksum_shouldbe(tcp_sum, sum));
406236192Sdelphij                                else
407236192Sdelphij                                        (void)printf(" (correct)");
408236192Sdelphij                        }
409190207Srpaulo                }
41075115Sfenner#ifdef INET6
411236192Sdelphij                else if (IP_V(ip) == 6 && ip6->ip6_plen) {
412236192Sdelphij                        if (TTEST2(tp->th_sport, length)) {
413236192Sdelphij                                sum = nextproto6_cksum(ip6, (const u_int8_t *)tp, length, IPPROTO_TCP);
414190207Srpaulo                                tcp_sum = EXTRACT_16BITS(&tp->th_sum);
415146773Ssam
416236192Sdelphij                                (void)printf(", cksum 0x%04x", tcp_sum);
417236192Sdelphij                                if (sum != 0)
418236192Sdelphij                                        (void)printf(" (incorrect -> 0x%04x)",
419236192Sdelphij                                            in_cksum_shouldbe(tcp_sum, sum));
420236192Sdelphij                                else
421236192Sdelphij                                        (void)printf(" (correct)");
422236192Sdelphij
423236192Sdelphij                        }
424190207Srpaulo                }
425236192Sdelphij#endif
426190207Srpaulo        }
42775115Sfenner
428190207Srpaulo        length -= hlen;
429214478Srpaulo        if (vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) {
430190207Srpaulo                (void)printf(", seq %u", seq);
43117680Spst
432190207Srpaulo                if (length > 0) {
433190207Srpaulo                        (void)printf(":%u", seq + length);
434190207Srpaulo                }
435190207Srpaulo        }
43617680Spst
437190207Srpaulo        if (flags & TH_ACK) {
438190207Srpaulo                (void)printf(", ack %u", ack);
439190207Srpaulo        }
44017680Spst
441190207Srpaulo        (void)printf(", win %d", win);
44226180Sfenner
443190207Srpaulo        if (flags & TH_URG)
444190207Srpaulo                (void)printf(", urg %d", urp);
445190207Srpaulo        /*
446190207Srpaulo         * Handle any options.
447190207Srpaulo         */
448190207Srpaulo        if (hlen > sizeof(*tp)) {
449190207Srpaulo                register const u_char *cp;
450190207Srpaulo                register u_int i, opt, datalen;
451190207Srpaulo                register u_int len;
452190207Srpaulo
453190207Srpaulo                hlen -= sizeof(*tp);
454190207Srpaulo                cp = (const u_char *)tp + sizeof(*tp);
455190207Srpaulo                printf(", options [");
456190207Srpaulo                while (hlen > 0) {
457190207Srpaulo                        if (ch != '\0')
458190207Srpaulo                                putchar(ch);
459190207Srpaulo                        TCHECK(*cp);
460190207Srpaulo                        opt = *cp++;
461190207Srpaulo                        if (ZEROLENOPT(opt))
462190207Srpaulo                                len = 1;
463190207Srpaulo                        else {
464190207Srpaulo                                TCHECK(*cp);
465190207Srpaulo                                len = *cp++;	/* total including type, len */
466190207Srpaulo                                if (len < 2 || len > hlen)
467190207Srpaulo                                        goto bad;
468190207Srpaulo                                --hlen;		/* account for length byte */
469190207Srpaulo                        }
470190207Srpaulo                        --hlen;			/* account for type byte */
471190207Srpaulo                        datalen = 0;
472190207Srpaulo
47326180Sfenner/* Bail if "l" bytes of data are not left or were not captured  */
47426180Sfenner#define LENCHECK(l) { if ((l) > hlen) goto bad; TCHECK2(*cp, l); }
47526180Sfenner
47617680Spst
477190207Srpaulo                        printf("%s", tok2str(tcp_option_values, "Unknown Option %u", opt));
47817680Spst
479190207Srpaulo                        switch (opt) {
48017680Spst
481190207Srpaulo                        case TCPOPT_MAXSEG:
482190207Srpaulo                                datalen = 2;
483190207Srpaulo                                LENCHECK(datalen);
484190207Srpaulo                                (void)printf(" %u", EXTRACT_16BITS(cp));
485190207Srpaulo                                break;
48617680Spst
487190207Srpaulo                        case TCPOPT_WSCALE:
488190207Srpaulo                                datalen = 1;
489190207Srpaulo                                LENCHECK(datalen);
490190207Srpaulo                                (void)printf(" %u", *cp);
491190207Srpaulo                                break;
49217680Spst
493190207Srpaulo                        case TCPOPT_SACK:
494190207Srpaulo                                datalen = len - 2;
495190207Srpaulo                                if (datalen % 8 != 0) {
496190207Srpaulo                                        (void)printf("malformed sack");
497190207Srpaulo                                } else {
498190207Srpaulo                                        u_int32_t s, e;
49917680Spst
500190207Srpaulo                                        (void)printf(" %d ", datalen / 8);
501190207Srpaulo                                        for (i = 0; i < datalen; i += 8) {
502190207Srpaulo                                                LENCHECK(i + 4);
503190207Srpaulo                                                s = EXTRACT_32BITS(cp + i);
504190207Srpaulo                                                LENCHECK(i + 8);
505190207Srpaulo                                                e = EXTRACT_32BITS(cp + i + 4);
506190207Srpaulo                                                if (threv) {
507190207Srpaulo                                                        s -= thseq;
508190207Srpaulo                                                        e -= thseq;
509190207Srpaulo                                                } else {
510190207Srpaulo                                                        s -= thack;
511190207Srpaulo                                                        e -= thack;
512190207Srpaulo                                                }
513190207Srpaulo                                                (void)printf("{%u:%u}", s, e);
514190207Srpaulo                                        }
515190207Srpaulo                                }
516190207Srpaulo                                break;
51717680Spst
518190207Srpaulo                        case TCPOPT_CC:
519190207Srpaulo                        case TCPOPT_CCNEW:
520190207Srpaulo                        case TCPOPT_CCECHO:
521190207Srpaulo                        case TCPOPT_ECHO:
522190207Srpaulo                        case TCPOPT_ECHOREPLY:
52356893Sfenner
524190207Srpaulo                                /*
525190207Srpaulo                                 * those options share their semantics.
526190207Srpaulo                                 * fall through
527190207Srpaulo                                 */
528190207Srpaulo                                datalen = 4;
529190207Srpaulo                                LENCHECK(datalen);
530190207Srpaulo                                (void)printf(" %u", EXTRACT_32BITS(cp));
531190207Srpaulo                                break;
53217680Spst
533190207Srpaulo                        case TCPOPT_TIMESTAMP:
534190207Srpaulo                                datalen = 8;
535190207Srpaulo                                LENCHECK(datalen);
536190207Srpaulo                                (void)printf(" val %u ecr %u",
537190207Srpaulo                                             EXTRACT_32BITS(cp),
538190207Srpaulo                                             EXTRACT_32BITS(cp + 4));
539190207Srpaulo                                break;
54017680Spst
541190207Srpaulo                        case TCPOPT_SIGNATURE:
542190207Srpaulo                                datalen = TCP_SIGLEN;
543190207Srpaulo                                LENCHECK(datalen);
544190207Srpaulo#ifdef HAVE_LIBCRYPTO
545190207Srpaulo                                switch (tcp_verify_signature(ip, tp,
546190207Srpaulo                                                             bp + TH_OFF(tp) * 4, length, cp)) {
54717680Spst
548190207Srpaulo                                case SIGNATURE_VALID:
549190207Srpaulo                                        (void)printf("valid");
550190207Srpaulo                                        break;
55117680Spst
552190207Srpaulo                                case SIGNATURE_INVALID:
553190207Srpaulo                                        (void)printf("invalid");
554190207Srpaulo                                        break;
55517680Spst
556190207Srpaulo                                case CANT_CHECK_SIGNATURE:
557190207Srpaulo                                        (void)printf("can't check - ");
558190207Srpaulo                                        for (i = 0; i < TCP_SIGLEN; ++i)
559190207Srpaulo                                                (void)printf("%02x", cp[i]);
560190207Srpaulo                                        break;
561190207Srpaulo                                }
562190207Srpaulo#else
563190207Srpaulo                                for (i = 0; i < TCP_SIGLEN; ++i)
564190207Srpaulo                                        (void)printf("%02x", cp[i]);
565190207Srpaulo#endif
566190207Srpaulo                                break;
56717680Spst
568190207Srpaulo                        case TCPOPT_AUTH:
569190207Srpaulo                                (void)printf("keyid %d", *cp++);
570190207Srpaulo                                datalen = len - 3;
571190207Srpaulo                                for (i = 0; i < datalen; ++i) {
572190207Srpaulo                                        LENCHECK(i);
573190207Srpaulo                                        (void)printf("%02x", cp[i]);
574190207Srpaulo                                }
575190207Srpaulo                                break;
57617680Spst
577146773Ssam
578190207Srpaulo                        case TCPOPT_EOL:
579190207Srpaulo                        case TCPOPT_NOP:
580190207Srpaulo                        case TCPOPT_SACKOK:
581190207Srpaulo                                /*
582190207Srpaulo                                 * Nothing interesting.
583190207Srpaulo                                 * fall through
584190207Srpaulo                                 */
585190207Srpaulo                                break;
586146773Ssam
587197829Srpaulo                        case TCPOPT_UTO:
588197829Srpaulo                                datalen = 2;
589197829Srpaulo                                LENCHECK(datalen);
590214478Srpaulo                                utoval = EXTRACT_16BITS(cp);
591214478Srpaulo                                (void)printf("0x%x", utoval);
592197829Srpaulo                                if (utoval & 0x0001)
593197829Srpaulo                                        utoval = (utoval >> 1) * 60;
594197829Srpaulo                                else
595197829Srpaulo                                        utoval >>= 1;
596197829Srpaulo                                (void)printf(" %u", utoval);
597197829Srpaulo                                break;
598197829Srpaulo
599190207Srpaulo                        default:
600190207Srpaulo                                datalen = len - 2;
601190207Srpaulo                                for (i = 0; i < datalen; ++i) {
602190207Srpaulo                                        LENCHECK(i);
603190207Srpaulo                                        (void)printf("%02x", cp[i]);
604190207Srpaulo                                }
605190207Srpaulo                                break;
606190207Srpaulo                        }
607146773Ssam
608190207Srpaulo                        /* Account for data printed */
609190207Srpaulo                        cp += datalen;
610190207Srpaulo                        hlen -= datalen;
611146773Ssam
612190207Srpaulo                        /* Check specification against observed length */
613190207Srpaulo                        ++datalen;			/* option octet */
614190207Srpaulo                        if (!ZEROLENOPT(opt))
615190207Srpaulo                                ++datalen;		/* size octet */
616190207Srpaulo                        if (datalen != len)
617190207Srpaulo                                (void)printf("[len %d]", len);
618190207Srpaulo                        ch = ',';
619190207Srpaulo                        if (opt == TCPOPT_EOL)
620190207Srpaulo                                break;
621190207Srpaulo                }
622190207Srpaulo                putchar(']');
623190207Srpaulo        }
62417680Spst
625190207Srpaulo        /*
626190207Srpaulo         * Print length field before crawling down the stack.
627190207Srpaulo         */
628190207Srpaulo        printf(", length %u", length);
62917680Spst
630190207Srpaulo        if (length <= 0)
631190207Srpaulo                return;
63256893Sfenner
633190207Srpaulo        /*
634190207Srpaulo         * Decode payload if necessary.
635190207Srpaulo         */
636190207Srpaulo        bp += TH_OFF(tp) * 4;
637190207Srpaulo        if ((flags & TH_RST) && vflag) {
638190207Srpaulo                print_tcp_rst_data(bp, length);
639190207Srpaulo                return;
640190207Srpaulo        }
64156893Sfenner
642252283Sdelphij        if (packettype) {
643252283Sdelphij                switch (packettype) {
644252283Sdelphij                case PT_ZMTP1:
645252283Sdelphij                        zmtp1_print(bp, length);
646252283Sdelphij                        break;
647252283Sdelphij                }
648252283Sdelphij                return;
649252283Sdelphij        }
650252283Sdelphij
651190207Srpaulo        if (sport == TELNET_PORT || dport == TELNET_PORT) {
652190207Srpaulo                if (!qflag && vflag)
653190207Srpaulo                        telnet_print(bp, length);
654190207Srpaulo        } else if (sport == BGP_PORT || dport == BGP_PORT)
655190207Srpaulo                bgp_print(bp, length);
656190207Srpaulo        else if (sport == PPTP_PORT || dport == PPTP_PORT)
657190207Srpaulo                pptp_print(bp);
65898524Sfenner#ifdef TCPDUMP_DO_SMB
659190207Srpaulo        else if (sport == NETBIOS_SSN_PORT || dport == NETBIOS_SSN_PORT)
660190207Srpaulo                nbt_tcp_print(bp, length);
661190207Srpaulo	else if (sport == SMB_PORT || dport == SMB_PORT)
662190207Srpaulo		smb_tcp_print(bp, length);
66398524Sfenner#endif
664190207Srpaulo        else if (sport == BEEP_PORT || dport == BEEP_PORT)
665190207Srpaulo                beep_print(bp, length);
666190207Srpaulo        else if (length > 2 &&
667190207Srpaulo                 (sport == NAMESERVER_PORT || dport == NAMESERVER_PORT ||
668190207Srpaulo                  sport == MULTICASTDNS_PORT || dport == MULTICASTDNS_PORT)) {
669190207Srpaulo                /*
670190207Srpaulo                 * TCP DNS query has 2byte length at the head.
671190207Srpaulo                 * XXX packet could be unaligned, it can go strange
672190207Srpaulo                 */
673190207Srpaulo                ns_print(bp + 2, length - 2, 0);
674190207Srpaulo        } else if (sport == MSDP_PORT || dport == MSDP_PORT) {
675190207Srpaulo                msdp_print(bp, length);
676236192Sdelphij        } else if (sport == RPKI_RTR_PORT || dport == RPKI_RTR_PORT) {
677236192Sdelphij                rpki_rtr_print(bp, length);
678190207Srpaulo        }
679190207Srpaulo        else if (length > 0 && (sport == LDP_PORT || dport == LDP_PORT)) {
680190207Srpaulo                ldp_print(bp, length);
681190207Srpaulo        }
682190207Srpaulo
683190207Srpaulo        return;
684190207Srpaulo bad:
685190207Srpaulo        fputs("[bad opt]", stdout);
686190207Srpaulo        if (ch != '\0')
687190207Srpaulo                putchar('>');
688190207Srpaulo        return;
689190207Srpaulo trunc:
690190207Srpaulo        fputs("[|tcp]", stdout);
691190207Srpaulo        if (ch != '\0')
692190207Srpaulo                putchar('>');
69317680Spst}
69417680Spst
69575115Sfenner/*
69675115Sfenner * RFC1122 says the following on data in RST segments:
69775115Sfenner *
69875115Sfenner *         4.2.2.12  RST Segment: RFC-793 Section 3.4
69975115Sfenner *
70075115Sfenner *            A TCP SHOULD allow a received RST segment to include data.
70175115Sfenner *
70275115Sfenner *            DISCUSSION
70375115Sfenner *                 It has been suggested that a RST segment could contain
70475115Sfenner *                 ASCII text that encoded and explained the cause of the
70575115Sfenner *                 RST.  No standard has yet been established for such
70675115Sfenner *                 data.
70775115Sfenner *
70875115Sfenner */
70975115Sfenner
71075115Sfennerstatic void
71175115Sfennerprint_tcp_rst_data(register const u_char *sp, u_int length)
71275115Sfenner{
713190207Srpaulo        int c;
71475115Sfenner
715190207Srpaulo        if (TTEST2(*sp, length))
716190207Srpaulo                printf(" [RST");
717190207Srpaulo        else
718190207Srpaulo                printf(" [!RST");
719190207Srpaulo        if (length > MAX_RST_DATA_LEN) {
720190207Srpaulo                length = MAX_RST_DATA_LEN;	/* can use -X for longer */
721190207Srpaulo                putchar('+');			/* indicate we truncate */
722190207Srpaulo        }
723190207Srpaulo        putchar(' ');
724190207Srpaulo        while (length-- && sp <= snapend) {
725190207Srpaulo                c = *sp++;
726190207Srpaulo                safeputchar(c);
727190207Srpaulo        }
728190207Srpaulo        putchar(']');
72975115Sfenner}
730146773Ssam
731146773Ssam#ifdef HAVE_LIBCRYPTO
732146773Ssamstatic int
733146773Ssamtcp_verify_signature(const struct ip *ip, const struct tcphdr *tp,
734190207Srpaulo                     const u_char *data, int length, const u_char *rcvsig)
735146773Ssam{
736146773Ssam        struct tcphdr tp1;
737190207Srpaulo        u_char sig[TCP_SIGLEN];
738190207Srpaulo        char zero_proto = 0;
739190207Srpaulo        MD5_CTX ctx;
740190207Srpaulo        u_int16_t savecsum, tlen;
741146773Ssam#ifdef INET6
742190207Srpaulo        struct ip6_hdr *ip6;
743190207Srpaulo        u_int32_t len32;
744190207Srpaulo        u_int8_t nxt;
745146773Ssam#endif
746146773Ssam
747214478Srpaulo	if (data + length > snapend) {
748214478Srpaulo		printf("snaplen too short, ");
749214478Srpaulo		return (CANT_CHECK_SIGNATURE);
750214478Srpaulo	}
751214478Srpaulo
752190207Srpaulo        tp1 = *tp;
753146773Ssam
754214478Srpaulo        if (sigsecret == NULL) {
755214478Srpaulo		printf("shared secret not supplied with -M, ");
756190207Srpaulo                return (CANT_CHECK_SIGNATURE);
757214478Srpaulo        }
758146773Ssam
759190207Srpaulo        MD5_Init(&ctx);
760190207Srpaulo        /*
761190207Srpaulo         * Step 1: Update MD5 hash with IP pseudo-header.
762190207Srpaulo         */
763190207Srpaulo        if (IP_V(ip) == 4) {
764190207Srpaulo                MD5_Update(&ctx, (char *)&ip->ip_src, sizeof(ip->ip_src));
765190207Srpaulo                MD5_Update(&ctx, (char *)&ip->ip_dst, sizeof(ip->ip_dst));
766190207Srpaulo                MD5_Update(&ctx, (char *)&zero_proto, sizeof(zero_proto));
767190207Srpaulo                MD5_Update(&ctx, (char *)&ip->ip_p, sizeof(ip->ip_p));
768190207Srpaulo                tlen = EXTRACT_16BITS(&ip->ip_len) - IP_HL(ip) * 4;
769190207Srpaulo                tlen = htons(tlen);
770190207Srpaulo                MD5_Update(&ctx, (char *)&tlen, sizeof(tlen));
771146773Ssam#ifdef INET6
772190207Srpaulo        } else if (IP_V(ip) == 6) {
773190207Srpaulo                ip6 = (struct ip6_hdr *)ip;
774190207Srpaulo                MD5_Update(&ctx, (char *)&ip6->ip6_src, sizeof(ip6->ip6_src));
775190207Srpaulo                MD5_Update(&ctx, (char *)&ip6->ip6_dst, sizeof(ip6->ip6_dst));
776214478Srpaulo                len32 = htonl(EXTRACT_16BITS(&ip6->ip6_plen));
777190207Srpaulo                MD5_Update(&ctx, (char *)&len32, sizeof(len32));
778190207Srpaulo                nxt = 0;
779190207Srpaulo                MD5_Update(&ctx, (char *)&nxt, sizeof(nxt));
780190207Srpaulo                MD5_Update(&ctx, (char *)&nxt, sizeof(nxt));
781190207Srpaulo                MD5_Update(&ctx, (char *)&nxt, sizeof(nxt));
782190207Srpaulo                nxt = IPPROTO_TCP;
783190207Srpaulo                MD5_Update(&ctx, (char *)&nxt, sizeof(nxt));
784146773Ssam#endif
785214478Srpaulo        } else {
786214478Srpaulo#ifdef INET6
787214478Srpaulo		printf("IP version not 4 or 6, ");
788214478Srpaulo#else
789214478Srpaulo		printf("IP version not 4, ");
790214478Srpaulo#endif
791190207Srpaulo                return (CANT_CHECK_SIGNATURE);
792214478Srpaulo        }
793146773Ssam
794190207Srpaulo        /*
795190207Srpaulo         * Step 2: Update MD5 hash with TCP header, excluding options.
796190207Srpaulo         * The TCP checksum must be set to zero.
797190207Srpaulo         */
798190207Srpaulo        savecsum = tp1.th_sum;
799190207Srpaulo        tp1.th_sum = 0;
800190207Srpaulo        MD5_Update(&ctx, (char *)&tp1, sizeof(struct tcphdr));
801190207Srpaulo        tp1.th_sum = savecsum;
802190207Srpaulo        /*
803190207Srpaulo         * Step 3: Update MD5 hash with TCP segment data, if present.
804190207Srpaulo         */
805190207Srpaulo        if (length > 0)
806190207Srpaulo                MD5_Update(&ctx, data, length);
807190207Srpaulo        /*
808190207Srpaulo         * Step 4: Update MD5 hash with shared secret.
809190207Srpaulo         */
810214478Srpaulo        MD5_Update(&ctx, sigsecret, strlen(sigsecret));
811190207Srpaulo        MD5_Final(sig, &ctx);
812146773Ssam
813190207Srpaulo        if (memcmp(rcvsig, sig, TCP_SIGLEN) == 0)
814190207Srpaulo                return (SIGNATURE_VALID);
815190207Srpaulo        else
816190207Srpaulo                return (SIGNATURE_INVALID);
817146773Ssam}
818146773Ssam#endif /* HAVE_LIBCRYPTO */
819190207Srpaulo
820190207Srpaulo/*
821190207Srpaulo * Local Variables:
822190207Srpaulo * c-style: whitesmith
823190207Srpaulo * c-basic-offset: 8
824190207Srpaulo * End:
825190207Srpaulo */
826