QSIG fully implemented
[obnox/wireshark/wip.git] / epan / dissectors / packet-teredo.c
1 /* packet-teredo.c  v.1.0
2  * Routines for Teredo packets disassembly
3  *   draft-huitema-v6ops-teredo-02.txt
4  *
5  * Copyright 2003, Ragi BEJJANI - 6WIND - <ragi.bejjani@6wind.com>
6  * Copyright 2003, Vincent JARDIN - 6WIND - <vincent.jardin@6wind.com>
7  * Copyright 2004, Remi DENIS-COURMONT
8  *
9  * $Id$
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <glib.h>
39 #include <epan/packet.h>
40 #include <epan/addr_resolv.h>
41 #include <epan/ipproto.h>
42 #include <epan/prefs.h>
43
44 #include "packet-ip.h"
45 #include <epan/tap.h>
46
47 #define UDP_PORT_TEREDO 3544
48
49 static int teredo_tap = -1;
50
51 static int proto_teredo = -1;
52
53 static int hf_teredo_auth = -1;
54 static int hf_teredo_auth_idlen = -1;
55 static int hf_teredo_auth_aulen = -1;
56 static int hf_teredo_auth_id = -1;
57 static int hf_teredo_auth_value = -1;
58 static int hf_teredo_auth_nonce = -1;
59 static int hf_teredo_auth_conf = -1;
60 static int hf_teredo_orig = -1;
61 static int hf_teredo_orig_port = -1;
62 static int hf_teredo_orig_addr = -1;
63
64 static gint ett_teredo = -1;
65 static gint ett_teredo_auth = -1, ett_teredo_orig = -1;
66
67 typedef struct {
68         guint16 th_indtyp;
69         guint8 th_cidlen;
70         guint8 th_authdlen;
71         guint8 th_nonce[8];
72         guint8 th_conf; 
73
74         guint8 th_ip_v_hl;  
75         guint16 th_header;
76         guint16 th_orgport;
77         guint32 th_iporgaddr;
78 } e_teredohdr;
79
80 static dissector_table_t teredo_dissector_table;
81 /*static heur_dissector_list_t heur_subdissector_list;*/
82 static dissector_handle_t data_handle;
83
84 static gboolean global_teredo_heur = FALSE;
85
86
87 static int
88 parse_teredo_auth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
89                         int offset, e_teredohdr *teredoh)
90 {
91         unsigned idlen, aulen;
92
93         if (check_col(pinfo->cinfo, COL_INFO))
94                 col_append_sep_str (pinfo->cinfo, COL_INFO, ", ",
95                                         "Authentication header");
96
97         teredoh->th_indtyp = 1;
98         offset += 2;
99
100         idlen = tvb_get_guint8(tvb, offset);
101         teredoh->th_cidlen = idlen;
102         offset++;
103
104         aulen = tvb_get_guint8(tvb, offset);
105         teredoh->th_authdlen = aulen;
106         offset++;
107
108         if (tree) {
109                 proto_item *ti;
110
111                 ti = proto_tree_add_item(tree, hf_teredo_auth, tvb, offset-4,
112                                                 13 + idlen + aulen, FALSE);
113                 tree = proto_item_add_subtree(ti, ett_teredo_auth);
114         
115                 proto_tree_add_item(tree, hf_teredo_auth_idlen, tvb,
116                                         offset - 2, 1, FALSE);
117                 proto_tree_add_item(tree, hf_teredo_auth_aulen, tvb,
118                                         offset - 1, 1, FALSE);
119
120                 /* idlen is usually zero */
121                 if (idlen) {
122                         proto_tree_add_item(tree, hf_teredo_auth_id, tvb,
123                                                 offset, idlen, FALSE);
124                         offset += idlen;
125                 }
126
127                 /* aulen is usually zero */
128                 if (aulen) {
129                         proto_tree_add_item(tree, hf_teredo_auth_value, tvb,
130                                                 offset, aulen, FALSE);
131                         offset += aulen;
132                 }
133
134                 proto_tree_add_item(tree, hf_teredo_auth_nonce, tvb,
135                                         offset, 8, FALSE);
136                 offset += 8;
137
138                 proto_tree_add_item(tree, hf_teredo_auth_conf, tvb,
139                                         offset, 1, FALSE);
140                 offset++;
141         }
142         else
143                 offset += idlen + aulen + 9;
144
145         tvb_memcpy(tvb, teredoh->th_nonce, offset - 9, 8);
146         teredoh->th_conf = tvb_get_guint8(tvb, offset - 1);
147
148         return offset;
149 }
150
151
152 static int
153 parse_teredo_orig(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
154                         int offset, e_teredohdr *teredoh)
155 {
156         proto_item *ti = NULL;
157
158         if (check_col(pinfo->cinfo, COL_INFO))
159                 col_append_sep_str (pinfo->cinfo, COL_INFO, ", ",
160                                         "Origin indication");
161
162         if (tree) {
163                 ti = proto_tree_add_item(tree, hf_teredo_orig, tvb, offset,
164                                                 8, FALSE);
165                 tree = proto_item_add_subtree(ti, ett_teredo_orig);
166         }
167         offset += 2;
168
169         teredoh->th_orgport = tvb_get_ntohs(tvb, offset);
170         if (tree) {
171                 /*
172                  * The "usual arithmetic conversions" will convert
173                  * "teredoh->th_orgport" to an "int" (because all
174                  * "unsigned short" values will fit in an "int"),
175                  * which will zero-extend it.  This means that
176                  * complementing it will turn all the zeroes in
177                  * the upper 16 bits into ones; we just want the
178                  * lower 16 bits (containing the port number)
179                  * complemented, with the result zero-extended.
180                  *
181                  * That's what the cast is for.
182                  */
183                 proto_tree_add_uint(tree, hf_teredo_orig_port, tvb,
184                                         offset, 2,
185                                         (guint16)~teredoh->th_orgport);
186         }
187         offset += 2;
188
189         teredoh->th_iporgaddr = tvb_get_ipv4(tvb, offset);
190         if (tree) {
191                 proto_tree_add_ipv4(tree, hf_teredo_orig_addr, tvb,
192                                         offset, 4, ~teredoh->th_iporgaddr);
193         }
194         offset += 4;
195
196         return offset;
197 }
198
199
200 /* Determine if there is a sub-dissector and call it.  This has been */
201 /* separated into a stand alone routine to other protocol dissectors */
202 /* can call to it, ie. socks    */
203
204
205 static void
206 decode_teredo_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,proto_tree *tree, int th_header)
207 {
208         tvbuff_t *next_tvb;
209
210         next_tvb = tvb_new_subset(tvb, offset, -1, -1);
211
212         if (dissector_try_port(teredo_dissector_table, th_header, next_tvb, pinfo, tree))
213                 return;
214
215         call_dissector(data_handle,next_tvb, pinfo, tree);  
216 }
217
218 static void
219 dissect_teredo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
220 {
221         proto_tree *teredo_tree;
222         proto_item *ti;
223         int        offset = 0;
224         static e_teredohdr teredohstruct[4], *teredoh;
225         static int teredoh_count = 0;
226
227         teredoh_count++;
228         if(teredoh_count>=4){
229                 teredoh_count=0;
230         }
231         teredoh = &teredohstruct[teredoh_count];
232
233         if (check_col(pinfo->cinfo, COL_PROTOCOL))
234                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Teredo");
235         if (check_col(pinfo->cinfo, COL_INFO))
236                 col_clear(pinfo->cinfo, COL_INFO);
237
238         if (tree) {
239                 ti = proto_tree_add_item(tree, proto_teredo, tvb, 0, -1, FALSE);
240                 teredo_tree = proto_item_add_subtree(ti, ett_teredo);
241         }
242         else
243                 teredo_tree = NULL;
244
245         teredoh->th_header  = tvb_get_ntohs(tvb, offset);
246
247         if (teredoh->th_header == 1) {
248                 offset = parse_teredo_auth(tvb, pinfo, teredo_tree,
249                                                 offset, teredoh);
250                 teredoh->th_header  = tvb_get_ntohs(tvb, offset);
251         }
252         else
253                 teredoh->th_indtyp  = 0;
254
255         if ( teredoh->th_header == 0 ) {
256                 offset = parse_teredo_orig(tvb, pinfo, teredo_tree,
257                                                 offset, teredoh);
258         }
259
260         teredoh->th_ip_v_hl = tvb_get_guint8(tvb, offset);
261
262         decode_teredo_ports(tvb, offset, pinfo, tree, teredoh->th_header /* , teredoh->th_orgport*/);
263         tap_queue_packet(teredo_tap, pinfo, teredoh);    
264 }
265
266
267 static gboolean
268 dissect_teredo_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
269 {
270         guint16 val;
271         int offset = 0;
272
273         if (!global_teredo_heur)
274                 return FALSE;
275
276         if (tvb_length_remaining(tvb, offset) < 40)
277                 return FALSE;
278
279         val = tvb_get_ntohs(tvb, offset);
280
281         if (val == 1) /* possible auth header */
282         {
283                 guint8 idlen, aulen;
284
285                 offset += 2;
286
287                 idlen = tvb_get_guint8(tvb, offset);
288                 offset++;
289
290                 aulen = tvb_get_guint8(tvb, offset);
291                 offset += 10;
292
293                 if (tvb_length_remaining(tvb, offset) < idlen + aulen + 40)
294                         return FALSE;
295
296                 offset += idlen + aulen;
297
298                 val = tvb_get_ntohs(tvb, offset);
299         }
300
301         if (val == 0) /* origin indication */
302         {
303                 offset += 8;
304
305                 if (tvb_length_remaining(tvb, offset) < 40)
306                         return FALSE;
307
308                 val = tvb_get_ntohs(tvb, offset);
309         }
310
311         /* 
312          * We have to check upper-layer packet a little bit otherwise we will
313          * match -almost- *ANY* packet.
314          * These checks are in the Teredo specification by the way.
315          * Unfortunately, that will cause false-negative if the snaplen is too
316          * short to get the packet entirely.
317          */
318         if ((val >> 12) == 6) /* IPv6 header */
319         {
320                 /* checks IPv6 payload length */
321                 val = tvb_get_ntohs(tvb, offset + 4);
322                 offset += 40;
323
324                 if (val > 65467)
325                         return FALSE; /* length too big for Teredo */
326
327                 if (tvb_length_remaining(tvb, offset) != val)
328                         return FALSE; /* length mismatch */
329
330                 dissect_teredo (tvb, pinfo, tree);
331                 return TRUE;
332         }
333
334         return FALSE; /* not an IPv6 packet */
335 }
336
337
338 void
339 proto_register_teredo(void)
340 {
341         static hf_register_info hf[] = {
342                 /* Authentication header */
343                 { &hf_teredo_auth,
344                 { "Teredo Authentication header", "teredo.auth",
345                   FT_NONE, BASE_NONE, NULL, 0x0,
346                   "Teredo Authentication header", HFILL }},
347   
348                 { &hf_teredo_auth_idlen,
349                 { "Client identifier length", "teredo.auth.idlen",
350                   FT_UINT8, BASE_DEC, NULL, 0x0,
351                   "Client identifier length (ID-len)", HFILL }},
352
353                 { &hf_teredo_auth_aulen,
354                 { "Authentication value length", "teredo.auth.aulen",
355                   FT_UINT8, BASE_DEC, NULL, 0x0,
356                   "Authentication value length (AU-len)", HFILL }},
357
358                 { &hf_teredo_auth_id,
359                 { "Client identifier", "teredo.auth.id",
360                   FT_BYTES, BASE_NONE, NULL, 0x0,
361                   "Client identifier (ID)", HFILL }},
362
363                 { &hf_teredo_auth_value,
364                 { "Authentication value", "teredo.auth.value",
365                   FT_BYTES, BASE_NONE, NULL, 0x0,
366                   "Authentication value (hash)", HFILL }},
367
368                 { &hf_teredo_auth_nonce,
369                 { "Nonce value", "teredo.auth.nonce",
370                   FT_BYTES, BASE_NONE, NULL, 0x0,
371                   "Nonce value prevents spoofing Teredo server.",
372                   HFILL }},
373
374                 { &hf_teredo_auth_conf,
375                 { "Confirmation byte", "teredo.auth.conf",
376                   FT_BYTES, BASE_NONE, NULL, 0x0,
377                   "Confirmation byte is zero upon successful authentication.",
378                   HFILL }},
379
380                 /* Origin indication */
381                 { &hf_teredo_orig,
382                 { "Teredo Origin Indication header", "teredo.orig",
383                   FT_NONE, BASE_NONE, NULL, 0x0,
384                   "Teredo Origin Indication", HFILL }},
385
386                 { &hf_teredo_orig_port,
387                 { "Origin UDP port", "teredo.orig.port",
388                   FT_UINT16, BASE_DEC, NULL, 0x0,
389                   "Origin UDP port", HFILL }},
390
391                 { &hf_teredo_orig_addr,
392                 { "Origin IPv4 address", "teredo.orig.addr",
393                   FT_IPv4, BASE_NONE, NULL, 0x0,
394                   "Origin IPv4 address", HFILL }},
395         };
396
397         static gint *ett[] = {
398                 &ett_teredo, &ett_teredo_auth, &ett_teredo_orig
399         };
400
401         module_t *teredo_module;
402
403         proto_teredo = proto_register_protocol(
404                 "Teredo IPv6 over UDP tunneling", "Teredo", "teredo");
405         proto_register_field_array(proto_teredo, hf, array_length(hf));
406         proto_register_subtree_array(ett, array_length(ett));
407
408 /* subdissector code */
409         teredo_dissector_table = register_dissector_table("teredo","Teredo ", FT_UINT16, BASE_DEC);
410
411         teredo_module = prefs_register_protocol(proto_teredo, NULL);
412
413         prefs_register_bool_preference(teredo_module, "heuristic_teredo",
414                 "Try to decode UDP packets as Teredo IPv6",
415                 "Check this to decode IPv6 traffic between Teredo clients and "
416                 "relays",
417                 &global_teredo_heur);
418
419 }
420
421 void
422 proto_reg_handoff_teredo(void)
423 {
424         dissector_handle_t teredo_handle;
425
426         teredo_handle = create_dissector_handle(dissect_teredo, proto_teredo);
427         data_handle   = find_dissector("ipv6");
428         teredo_tap    = register_tap("teredo");
429
430         dissector_add("udp.port", UDP_PORT_TEREDO, teredo_handle);
431         heur_dissector_add("udp", dissect_teredo_heur, proto_teredo);
432 }
433