Add tvbuff class.
[obnox/wireshark/wip.git] / packet-tcp.c
1 /* packet-tcp.c
2  * Routines for TCP packet disassembly
3  *
4  * $Id: packet-tcp.c,v 1.74 2000/05/11 08:15:52 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 # include <netinet/in.h>
36 #endif
37
38 #include <stdio.h>
39 #include <glib.h>
40 #include "globals.h"
41 #include "resolv.h"
42 #include "follow.h"
43
44 #ifdef NEED_SNPRINTF_H
45 # ifdef HAVE_STDARG_H
46 #  include <stdarg.h>
47 # else
48 #  include <varargs.h>
49 # endif
50 # include "snprintf.h"
51 #endif
52
53 #include "plugins.h"
54 #include "packet-tcp.h"
55
56 #include "packet-ip.h"
57 #include "packet-rpc.h"
58
59 extern FILE* data_out_file;
60
61 guint16 tcp_urgent_pointer;
62
63 static gchar info_str[COL_MAX_LEN];
64 static int   info_len;
65
66 static int proto_tcp = -1;
67 static int hf_tcp_srcport = -1;
68 static int hf_tcp_dstport = -1;
69 static int hf_tcp_port = -1;
70 static int hf_tcp_seq = -1;
71 static int hf_tcp_ack = -1;
72 static int hf_tcp_hdr_len = -1;
73 static int hf_tcp_flags = -1;
74 static int hf_tcp_flags_urg = -1;
75 static int hf_tcp_flags_ack = -1;
76 static int hf_tcp_flags_push = -1;
77 static int hf_tcp_flags_reset = -1;
78 static int hf_tcp_flags_syn = -1;
79 static int hf_tcp_flags_fin = -1;
80 static int hf_tcp_window_size = -1;
81 static int hf_tcp_checksum = -1;
82 static int hf_tcp_urgent_pointer = -1;
83
84 static gint ett_tcp = -1;
85 static gint ett_tcp_flags = -1;
86 static gint ett_tcp_options = -1;
87 static gint ett_tcp_option_sack = -1;
88
89 static dissector_table_t subdissector_table;
90 static heur_dissector_list_t heur_subdissector_list;
91
92 /* TCP Ports */
93
94 #define TCP_PORT_SMTP                   25
95
96 /* TCP structs and definitions */
97
98 typedef struct _e_tcphdr {
99   guint16 th_sport;
100   guint16 th_dport;
101   guint32 th_seq;
102   guint32 th_ack;
103   guint8  th_off_x2; /* combines th_off and th_x2 */
104   guint8  th_flags;
105 #define TH_FIN  0x01
106 #define TH_SYN  0x02
107 #define TH_RST  0x04
108 #define TH_PUSH 0x08
109 #define TH_ACK  0x10
110 #define TH_URG  0x20
111   guint16 th_win;
112   guint16 th_sum;
113   guint16 th_urp;
114 } e_tcphdr;
115
116 /*
117  *      TCP option
118  */
119  
120 #define TCPOPT_NOP              1       /* Padding */
121 #define TCPOPT_EOL              0       /* End of options */
122 #define TCPOPT_MSS              2       /* Segment size negotiating */
123 #define TCPOPT_WINDOW           3       /* Window scaling */
124 #define TCPOPT_SACK_PERM        4       /* SACK Permitted */
125 #define TCPOPT_SACK             5       /* SACK Block */
126 #define TCPOPT_ECHO             6
127 #define TCPOPT_ECHOREPLY        7
128 #define TCPOPT_TIMESTAMP        8       /* Better RTT estimations/PAWS */
129 #define TCPOPT_CC               11
130 #define TCPOPT_CCNEW            12
131 #define TCPOPT_CCECHO           13
132
133 /*
134  *     TCP option lengths
135  */
136
137 #define TCPOLEN_MSS            4
138 #define TCPOLEN_WINDOW         3
139 #define TCPOLEN_SACK_PERM      2
140 #define TCPOLEN_SACK_MIN       2
141 #define TCPOLEN_ECHO           6
142 #define TCPOLEN_ECHOREPLY      6
143 #define TCPOLEN_TIMESTAMP      10
144 #define TCPOLEN_CC             6
145 #define TCPOLEN_CCNEW          6
146 #define TCPOLEN_CCECHO         6
147
148 static void
149 tcp_info_append_uint(const char *abbrev, guint32 val) {
150   int add_len = 0;
151   
152   if (info_len > 0)
153   if(info_len > 0)
154     add_len = snprintf(&info_str[info_len], COL_MAX_LEN - info_len, " %s=%u",
155       abbrev, val);
156   if (add_len > 0)
157     info_len += add_len;
158 }
159
160 static void
161 dissect_tcpopt_maxseg(const ip_tcp_opt *optp, const u_char *opd,
162     int offset, guint optlen, proto_tree *opt_tree)
163 {
164   proto_tree_add_text(opt_tree, NullTVB, offset,      optlen,
165                         "%s: %u bytes", optp->name, pntohs(opd));
166   tcp_info_append_uint("MSS", pntohs(opd));
167 }
168
169 static void
170 dissect_tcpopt_wscale(const ip_tcp_opt *optp, const u_char *opd,
171     int offset, guint optlen, proto_tree *opt_tree)
172 {
173   proto_tree_add_text(opt_tree, NullTVB, offset,      optlen,
174                         "%s: %u bytes", optp->name, *opd);
175   tcp_info_append_uint("WS", *opd);
176 }
177
178 static void
179 dissect_tcpopt_sack(const ip_tcp_opt *optp, const u_char *opd,
180     int offset, guint optlen, proto_tree *opt_tree)
181 {
182   proto_tree *field_tree = NULL;
183   proto_item *tf;
184   guint leftedge, rightedge;
185
186   tf = proto_tree_add_text(opt_tree, NullTVB, offset,      optlen, "%s:", optp->name);
187   offset += 2;  /* skip past type and length */
188   optlen -= 2;  /* subtract size of type and length */
189   while (optlen > 0) {
190     if (field_tree == NULL) {
191       /* Haven't yet made a subtree out of this option.  Do so. */
192       field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
193     }
194     if (optlen < 4) {
195       proto_tree_add_text(field_tree, NullTVB, offset,      optlen,
196         "(suboption would go past end of option)");
197       break;
198     }
199     /* XXX - check whether it goes past end of packet */
200     leftedge = pntohl(opd);
201     opd += 4;
202     optlen -= 4;
203     if (optlen < 4) {
204       proto_tree_add_text(field_tree, NullTVB, offset,      optlen,
205         "(suboption would go past end of option)");
206       break;
207     }
208     /* XXX - check whether it goes past end of packet */
209     rightedge = pntohl(opd);
210     opd += 4;
211     optlen -= 4;
212     proto_tree_add_text(field_tree, NullTVB, offset,      8,
213         "left edge = %u, right edge = %u", leftedge, rightedge);
214     tcp_info_append_uint("SLE", leftedge);
215     tcp_info_append_uint("SRE", rightedge);
216     offset += 8;
217   }
218 }
219
220 static void
221 dissect_tcpopt_echo(const ip_tcp_opt *optp, const u_char *opd,
222     int offset, guint optlen, proto_tree *opt_tree)
223 {
224   proto_tree_add_text(opt_tree, NullTVB, offset,      optlen,
225                         "%s: %u", optp->name, pntohl(opd));
226   tcp_info_append_uint("ECHO", pntohl(opd));
227 }
228
229 static void
230 dissect_tcpopt_timestamp(const ip_tcp_opt *optp, const u_char *opd,
231     int offset, guint optlen, proto_tree *opt_tree)
232 {
233   proto_tree_add_text(opt_tree, NullTVB, offset,      optlen,
234     "%s: tsval %u, tsecr %u", optp->name, pntohl(opd), pntohl(opd + 4));
235   tcp_info_append_uint("TSV", pntohl(opd));
236   tcp_info_append_uint("TSER", pntohl(opd + 4));
237 }
238
239 static void
240 dissect_tcpopt_cc(const ip_tcp_opt *optp, const u_char *opd,
241     int offset, guint optlen, proto_tree *opt_tree)
242 {
243   proto_tree_add_text(opt_tree, NullTVB, offset,      optlen,
244                         "%s: %u", optp->name, pntohl(opd));
245   tcp_info_append_uint("CC", pntohl(opd));
246 }
247
248 static const ip_tcp_opt tcpopts[] = {
249   {
250     TCPOPT_EOL,
251     "EOL",
252     NULL,
253     NO_LENGTH,
254     0,
255     NULL,
256   },
257   {
258     TCPOPT_NOP,
259     "NOP",
260     NULL,
261     NO_LENGTH,
262     0,
263     NULL,
264   },
265   {
266     TCPOPT_MSS,
267     "Maximum segment size",
268     NULL,
269     FIXED_LENGTH,
270     TCPOLEN_MSS,
271     dissect_tcpopt_maxseg
272   },
273   {
274     TCPOPT_WINDOW,
275     "Window scale",
276     NULL,
277     FIXED_LENGTH,
278     TCPOLEN_WINDOW,
279     dissect_tcpopt_wscale
280   },
281   {
282     TCPOPT_SACK_PERM,
283     "SACK permitted",
284     NULL,
285     FIXED_LENGTH,
286     TCPOLEN_SACK_PERM,
287     NULL,
288   },
289   {
290     TCPOPT_SACK,
291     "SACK",
292     &ett_tcp_option_sack,
293     VARIABLE_LENGTH,
294     TCPOLEN_SACK_MIN,
295     dissect_tcpopt_sack
296   },
297   {
298     TCPOPT_ECHO,
299     "Echo",
300     NULL,
301     FIXED_LENGTH,
302     TCPOLEN_ECHO,
303     dissect_tcpopt_echo
304   },
305   {
306     TCPOPT_ECHOREPLY,
307     "Echo reply",
308     NULL,
309     FIXED_LENGTH,
310     TCPOLEN_ECHOREPLY,
311     dissect_tcpopt_echo
312   },
313   {
314     TCPOPT_TIMESTAMP,
315     "Time stamp",
316     NULL,
317     FIXED_LENGTH,
318     TCPOLEN_TIMESTAMP,
319     dissect_tcpopt_timestamp
320   },
321   {
322     TCPOPT_CC,
323     "CC",
324     NULL,
325     FIXED_LENGTH,
326     TCPOLEN_CC,
327     dissect_tcpopt_cc
328   },
329   {
330     TCPOPT_CCNEW,
331     "CC.NEW",
332     NULL,
333     FIXED_LENGTH,
334     TCPOPT_CCNEW,
335     dissect_tcpopt_cc
336   },
337   {
338     TCPOPT_CCECHO,
339     "CC.ECHO",
340     NULL,
341     FIXED_LENGTH,
342     TCPOLEN_CCECHO,
343     dissect_tcpopt_cc
344   }
345 };
346
347 #define N_TCP_OPTS      (sizeof tcpopts / sizeof tcpopts[0])
348
349 /* TCP flags flag */
350 static const true_false_string flags_set_truth = {
351   "Set",
352   "Not set"
353 };
354
355
356 /* Determine if there is a sub-dissector and call it.  This has been */
357 /* separated into a stand alone routine to other protocol dissectors */
358 /* can call to it, ie. socks    */
359
360 void
361 decode_tcp_ports( const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
362         int src_port, int dst_port) {
363   dissector_t sub_dissector;
364
365 /* determine if this packet is part of a conversation and call dissector */
366 /* for the conversation if available */
367
368   sub_dissector = find_conversation_dissector( &pi.src, &pi.dst, PT_TCP,
369                 src_port, dst_port);
370   if (sub_dissector){
371         (sub_dissector)(pd, offset, fd, tree);
372         return;
373   }
374
375   /* ONC RPC.  We can't base this on anything in the TCP header; we have
376      to look at the payload.  If "dissect_rpc()" returns TRUE, it was
377      an RPC packet, otherwise it's some other type of packet. */
378   if (dissect_rpc(pd, offset, fd, tree))
379     return;
380
381   /* try to apply the plugins */
382 #ifdef HAVE_PLUGINS
383   {
384     plugin *pt_plug = plugin_list;
385
386     if (enabled_plugins_number > 0) {
387       while (pt_plug) {
388         if (pt_plug->enabled && !strcmp(pt_plug->protocol, "tcp") &&
389             tree && dfilter_apply(pt_plug->filter, tree, pd, fd->cap_len)) {
390           pt_plug->dissector(pd, offset, fd, tree);
391           return;
392         }
393         pt_plug = pt_plug->next;
394       }
395     }
396   }
397 #endif
398
399   /* do lookup with the subdissector table */
400   if (dissector_try_port(subdissector_table, src_port, pd, offset, fd, tree) ||
401       dissector_try_port(subdissector_table, dst_port, pd, offset, fd, tree))
402     return;
403
404   /* do lookup with the heuristic subdissector table */
405   if (dissector_try_heuristic(heur_subdissector_list, pd, offset, fd, tree))
406     return;
407
408   /* Oh, well, we don't know this; dissect it as data. */
409   dissect_data(pd, offset, fd, tree);
410 }
411
412
413 static void
414 dissect_tcp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
415   e_tcphdr   th;
416   proto_tree *tcp_tree = NULL, *field_tree = NULL;
417   proto_item *ti, *tf;
418   gchar      flags[64] = "<None>";
419   gchar     *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG"};
420   gint       fpos = 0, i;
421   guint      bpos;
422   guint      hlen;
423   guint      optlen;
424   guint      packet_max = pi.len;
425
426   /* To do: Check for {cap len,pkt len} < struct len */
427   /* Avoids alignment problems on many architectures. */
428   memcpy(&th, &pd[offset], sizeof(e_tcphdr));
429   th.th_sport = ntohs(th.th_sport);
430   th.th_dport = ntohs(th.th_dport);
431   th.th_win   = ntohs(th.th_win);
432   th.th_sum   = ntohs(th.th_sum);
433   th.th_urp   = ntohs(th.th_urp);
434   th.th_seq   = ntohl(th.th_seq);
435   th.th_ack   = ntohl(th.th_ack);
436
437   /* Export the urgent pointer, for the benefit of protocols such as
438      rlogin. */
439   tcp_urgent_pointer = th.th_urp;
440  
441   info_len = 0;
442
443   if (check_col(fd, COL_PROTOCOL) || tree) {  
444     for (i = 0; i < 6; i++) {
445       bpos = 1 << i;
446       if (th.th_flags & bpos) {
447         if (fpos) {
448           strcpy(&flags[fpos], ", ");
449           fpos += 2;
450         }
451         strcpy(&flags[fpos], fstr[i]);
452         fpos += 3;
453       }
454     }
455     flags[fpos] = '\0';
456   }
457   
458   hlen = hi_nibble(th.th_off_x2) * 4;  /* TCP header length, in bytes */
459
460   if (check_col(fd, COL_PROTOCOL))
461     col_add_str(fd, COL_PROTOCOL, "TCP");
462   if (check_col(fd, COL_INFO)) {
463     /* Copy the data into info_str in case one of the option handling
464        routines needs to append to it. */
465     if (th.th_flags & TH_URG)
466       info_len = snprintf(info_str, COL_MAX_LEN, "%s > %s [%s] Seq=%u Ack=%u Win=%u Urg=%u Len=%d",
467         get_tcp_port(th.th_sport), get_tcp_port(th.th_dport), flags,
468         th.th_seq, th.th_ack, th.th_win, th.th_urp, pi.len - offset - hlen);
469     else
470       info_len = snprintf(info_str, COL_MAX_LEN, "%s > %s [%s] Seq=%u Ack=%u Win=%u Len=%d",
471         get_tcp_port(th.th_sport), get_tcp_port(th.th_dport), flags,
472         th.th_seq, th.th_ack, th.th_win, pi.len - offset - hlen);
473     /* The info column is actually written after the options are decoded */
474   }
475   
476   if (tree) {
477     ti = proto_tree_add_protocol_format(tree, proto_tcp, NullTVB, offset, hlen, "Transmission Control Protocol, Src Port: %s (%u), Dst Port: %s (%u), Seq: %u, Ack: %u", get_tcp_port(th.th_sport), th.th_sport, get_tcp_port(th.th_dport), th.th_dport, th.th_seq, th.th_ack);
478     tcp_tree = proto_item_add_subtree(ti, ett_tcp);
479     proto_tree_add_uint_format(tcp_tree, hf_tcp_srcport, NullTVB, offset, 2, th.th_sport,
480         "Source port: %s (%u)", get_tcp_port(th.th_sport), th.th_sport);
481     proto_tree_add_uint_format(tcp_tree, hf_tcp_dstport, NullTVB, offset + 2, 2, th.th_dport,
482         "Destination port: %s (%u)", get_tcp_port(th.th_dport), th.th_dport);
483     proto_tree_add_item_hidden(tcp_tree, hf_tcp_port, NullTVB, offset, 2, th.th_sport);
484     proto_tree_add_item_hidden(tcp_tree, hf_tcp_port, NullTVB, offset + 2, 2, th.th_dport);
485     proto_tree_add_item(tcp_tree, hf_tcp_seq, NullTVB, offset + 4, 4, th.th_seq);
486     if (th.th_flags & TH_ACK)
487       proto_tree_add_item(tcp_tree, hf_tcp_ack, NullTVB, offset + 8, 4, th.th_ack);
488     proto_tree_add_uint_format(tcp_tree, hf_tcp_hdr_len, NullTVB, offset + 12, 1, hlen,
489         "Header length: %u bytes", hlen);
490     tf = proto_tree_add_uint_format(tcp_tree, hf_tcp_flags, NullTVB, offset + 13, 1,
491         th.th_flags, "Flags: 0x%04x (%s)", th.th_flags, flags);
492     field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
493     proto_tree_add_item(field_tree, hf_tcp_flags_urg, NullTVB, offset + 13, 1, th.th_flags);
494     proto_tree_add_item(field_tree, hf_tcp_flags_ack, NullTVB, offset + 13, 1, th.th_flags);
495     proto_tree_add_item(field_tree, hf_tcp_flags_push, NullTVB, offset + 13, 1, th.th_flags);
496     proto_tree_add_item(field_tree, hf_tcp_flags_reset, NullTVB, offset + 13, 1, th.th_flags);
497     proto_tree_add_item(field_tree, hf_tcp_flags_syn, NullTVB, offset + 13, 1, th.th_flags);
498     proto_tree_add_item(field_tree, hf_tcp_flags_fin, NullTVB, offset + 13, 1, th.th_flags);
499     proto_tree_add_item(tcp_tree, hf_tcp_window_size, NullTVB, offset + 14, 2, th.th_win);
500     proto_tree_add_item(tcp_tree, hf_tcp_checksum, NullTVB, offset + 16, 2, th.th_sum);
501     if (th.th_flags & TH_URG)
502       proto_tree_add_item(tcp_tree, hf_tcp_urgent_pointer, NullTVB, offset + 18, 2, th.th_urp);
503   }
504
505   /* Decode TCP options, if any. */
506   if (tree  && hlen > sizeof (e_tcphdr)) {
507     /* There's more than just the fixed-length header.  Decode the
508        options. */
509     optlen = hlen - sizeof (e_tcphdr); /* length of options, in bytes */
510     tf = proto_tree_add_text(tcp_tree, NullTVB, offset +  20, optlen,
511       "Options: (%d bytes)", optlen);
512     field_tree = proto_item_add_subtree(tf, ett_tcp_options);
513     dissect_ip_tcp_options(&pd[offset + 20], offset + 20, optlen,
514       tcpopts, N_TCP_OPTS, TCPOPT_EOL, field_tree);
515   }
516
517   if (check_col(fd, COL_INFO))
518     col_add_str(fd, COL_INFO, info_str);
519
520   /* Skip over header + options */
521   offset += hlen;
522
523   pi.ptype = PT_TCP;
524   pi.srcport = th.th_sport;
525   pi.destport = th.th_dport;
526   
527   /* Check the packet length to see if there's more data
528      (it could be an ACK-only packet) */
529   if (packet_max > offset) 
530     decode_tcp_ports( pd, offset, fd, tree, th.th_sport, th.th_dport);
531  
532   if( data_out_file ) {
533     reassemble_tcp( th.th_seq,          /* sequence number */
534         ( pi.len - offset ),            /* data length */
535         ( pd+offset ),                  /* data */
536         ( pi.captured_len - offset ),   /* captured data length */
537         ( th.th_flags & TH_SYN ),       /* is syn set? */
538         &pi.net_src,
539         &pi.net_dst,
540         pi.srcport,
541         pi.destport);
542   }
543 }
544
545 void
546 proto_register_tcp(void)
547 {
548         static hf_register_info hf[] = {
549
550                 { &hf_tcp_srcport,
551                 { "Source Port",                "tcp.srcport", FT_UINT16, BASE_DEC, NULL, 0x0,
552                         "" }},
553
554                 { &hf_tcp_dstport,
555                 { "Destination Port",           "tcp.dstport", FT_UINT16, BASE_DEC, NULL, 0x0,
556                         "" }},
557
558                 { &hf_tcp_port,
559                 { "Source or Destination Port", "tcp.port", FT_UINT16, BASE_DEC, NULL, 0x0,
560                         "" }},
561
562                 { &hf_tcp_seq,
563                 { "Sequence number",            "tcp.seq", FT_UINT32, BASE_DEC, NULL, 0x0,
564                         "" }},
565
566                 { &hf_tcp_ack,
567                 { "Acknowledgement number",     "tcp.ack", FT_UINT32, BASE_DEC, NULL, 0x0,
568                         "" }},
569
570                 { &hf_tcp_hdr_len,
571                 { "Header Length",              "tcp.hdr_len", FT_UINT8, BASE_DEC, NULL, 0x0,
572                         "" }},
573
574                 { &hf_tcp_flags,
575                 { "Flags",                      "tcp.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
576                         "" }},
577
578                 { &hf_tcp_flags_urg,
579                 { "Urgent",                     "tcp.flags.urg", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_URG,
580                         "" }},
581
582                 { &hf_tcp_flags_ack,
583                 { "Acknowledgment",             "tcp.flags.ack", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_ACK,
584                         "" }},
585
586                 { &hf_tcp_flags_push,
587                 { "Push",                       "tcp.flags.push", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_PUSH,
588                         "" }},
589
590                 { &hf_tcp_flags_reset,
591                 { "Reset",                      "tcp.flags.reset", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_RST,
592                         "" }},
593
594                 { &hf_tcp_flags_syn,
595                 { "Syn",                        "tcp.flags.syn", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_SYN,
596                         "" }},
597
598                 { &hf_tcp_flags_fin,
599                 { "Fin",                        "tcp.flags.fin", FT_BOOLEAN, 8, TFS(&flags_set_truth), TH_FIN,
600                         "" }},
601
602                 { &hf_tcp_window_size,
603                 { "Window size",                "tcp.window_size", FT_UINT16, BASE_DEC, NULL, 0x0,
604                         "" }},
605
606                 { &hf_tcp_checksum,
607                 { "Checksum",                   "tcp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
608                         "" }},
609
610                 { &hf_tcp_urgent_pointer,
611                 { "Urgent pointer",             "tcp.urgent_pointer", FT_UINT16, BASE_DEC, NULL, 0x0,
612                         "" }},
613         };
614         static gint *ett[] = {
615                 &ett_tcp,
616                 &ett_tcp_flags,
617                 &ett_tcp_options,
618                 &ett_tcp_option_sack,
619         };
620
621         proto_tcp = proto_register_protocol ("Transmission Control Protocol", "tcp");
622         proto_register_field_array(proto_tcp, hf, array_length(hf));
623         proto_register_subtree_array(ett, array_length(ett));
624
625 /* subdissector code */
626         subdissector_table = register_dissector_table("tcp.port");
627         register_heur_dissector_list("tcp", &heur_subdissector_list);
628 }
629
630 void
631 proto_reg_handoff_tcp(void)
632 {
633         dissector_add("ip.proto", IP_PROTO_TCP, dissect_tcp);
634 }