/src/wireshark/epan/dissectors/packet-teredo.c
Line | Count | Source (jump to first uncovered line) |
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 | | * Wireshark - Network traffic analyzer |
10 | | * By Gerald Combs <gerald@wireshark.org> |
11 | | * Copyright 1998 Gerald Combs |
12 | | * |
13 | | * SPDX-License-Identifier: GPL-2.0-or-later |
14 | | */ |
15 | | |
16 | | #include "config.h" |
17 | | |
18 | | #include <epan/packet.h> |
19 | | #include <epan/prefs.h> |
20 | | |
21 | | #include <epan/tap.h> |
22 | | |
23 | 14 | #define UDP_PORT_TEREDO 3544 |
24 | | |
25 | | void proto_reg_handoff_teredo(void); |
26 | | void proto_register_teredo(void); |
27 | | |
28 | | static int teredo_tap; |
29 | | |
30 | | static int proto_teredo; |
31 | | |
32 | | static int hf_teredo_auth; |
33 | | static int hf_teredo_auth_idlen; |
34 | | static int hf_teredo_auth_aulen; |
35 | | static int hf_teredo_auth_id; |
36 | | static int hf_teredo_auth_value; |
37 | | static int hf_teredo_auth_nonce; |
38 | | static int hf_teredo_auth_conf; |
39 | | static int hf_teredo_orig; |
40 | | static int hf_teredo_orig_port; |
41 | | static int hf_teredo_orig_addr; |
42 | | |
43 | | static int ett_teredo; |
44 | | static int ett_teredo_auth; |
45 | | static int ett_teredo_orig; |
46 | | |
47 | | typedef struct { |
48 | | uint16_t th_indtyp; |
49 | | uint8_t th_cidlen; |
50 | | uint8_t th_authdlen; |
51 | | uint8_t th_nonce[8]; |
52 | | uint8_t th_conf; |
53 | | |
54 | | uint8_t th_ip_v_hl; |
55 | | uint16_t th_header; |
56 | | uint16_t th_orgport; |
57 | | uint32_t th_iporgaddr; |
58 | | } e_teredohdr; |
59 | | |
60 | | static dissector_table_t teredo_dissector_table; |
61 | | /*static heur_dissector_list_t heur_subdissector_list;*/ |
62 | | static dissector_handle_t teredo_handle; |
63 | | static dissector_handle_t data_handle; |
64 | | |
65 | | static int |
66 | | parse_teredo_auth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
67 | | int offset, e_teredohdr *teredoh) |
68 | 5 | { |
69 | 5 | unsigned idlen, aulen; |
70 | | |
71 | 5 | col_append_sep_str (pinfo->cinfo, COL_INFO, ", ", |
72 | 5 | "Authentication header"); |
73 | | |
74 | 5 | teredoh->th_indtyp = 1; |
75 | 5 | offset += 2; |
76 | | |
77 | 5 | idlen = tvb_get_uint8(tvb, offset); |
78 | 5 | teredoh->th_cidlen = idlen; |
79 | 5 | offset++; |
80 | | |
81 | 5 | aulen = tvb_get_uint8(tvb, offset); |
82 | 5 | teredoh->th_authdlen = aulen; |
83 | 5 | offset++; |
84 | | |
85 | 5 | if (tree) { |
86 | 5 | proto_item *ti; |
87 | | |
88 | 5 | ti = proto_tree_add_item(tree, hf_teredo_auth, tvb, offset-4, |
89 | 5 | 13 + idlen + aulen, ENC_NA); |
90 | 5 | tree = proto_item_add_subtree(ti, ett_teredo_auth); |
91 | | |
92 | 5 | proto_tree_add_item(tree, hf_teredo_auth_idlen, tvb, |
93 | 5 | offset - 2, 1, ENC_BIG_ENDIAN); |
94 | 5 | proto_tree_add_item(tree, hf_teredo_auth_aulen, tvb, |
95 | 5 | offset - 1, 1, ENC_BIG_ENDIAN); |
96 | | |
97 | | /* idlen is usually zero */ |
98 | 5 | if (idlen) { |
99 | 2 | proto_tree_add_item(tree, hf_teredo_auth_id, tvb, |
100 | 2 | offset, idlen, ENC_NA); |
101 | 2 | offset += idlen; |
102 | 2 | } |
103 | | |
104 | | /* aulen is usually zero */ |
105 | 5 | if (aulen) { |
106 | 3 | proto_tree_add_item(tree, hf_teredo_auth_value, tvb, |
107 | 3 | offset, aulen, ENC_NA); |
108 | 3 | offset += aulen; |
109 | 3 | } |
110 | | |
111 | 5 | proto_tree_add_item(tree, hf_teredo_auth_nonce, tvb, |
112 | 5 | offset, 8, ENC_NA); |
113 | 5 | offset += 8; |
114 | | |
115 | 5 | proto_tree_add_item(tree, hf_teredo_auth_conf, tvb, |
116 | 5 | offset, 1, ENC_NA); |
117 | 5 | offset++; |
118 | 5 | } |
119 | 0 | else |
120 | 0 | offset += idlen + aulen + 9; |
121 | | |
122 | 5 | tvb_memcpy(tvb, teredoh->th_nonce, offset - 9, 8); |
123 | 5 | teredoh->th_conf = tvb_get_uint8(tvb, offset - 1); |
124 | | |
125 | 5 | return offset; |
126 | 5 | } |
127 | | |
128 | | |
129 | | static int |
130 | | parse_teredo_orig(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
131 | | int offset, e_teredohdr *teredoh) |
132 | 5 | { |
133 | 5 | proto_item *ti = NULL; |
134 | | |
135 | 5 | col_append_sep_str (pinfo->cinfo, COL_INFO, ", ", |
136 | 5 | "Origin indication"); |
137 | | |
138 | 5 | if (tree) { |
139 | 5 | ti = proto_tree_add_item(tree, hf_teredo_orig, tvb, offset, |
140 | 5 | 8, ENC_NA); |
141 | 5 | tree = proto_item_add_subtree(ti, ett_teredo_orig); |
142 | 5 | } |
143 | 5 | offset += 2; |
144 | | |
145 | 5 | teredoh->th_orgport = tvb_get_ntohs(tvb, offset); |
146 | 5 | if (tree) { |
147 | | /* |
148 | | * The "usual arithmetic conversions" will convert |
149 | | * "teredoh->th_orgport" to an "int" (because all |
150 | | * "unsigned short" values will fit in an "int"), |
151 | | * which will zero-extend it. This means that |
152 | | * complementing it will turn all the zeroes in |
153 | | * the upper 16 bits into ones; we just want the |
154 | | * lower 16 bits (containing the port number) |
155 | | * complemented, with the result zero-extended. |
156 | | * |
157 | | * That's what the cast is for. |
158 | | */ |
159 | 5 | proto_tree_add_uint(tree, hf_teredo_orig_port, tvb, |
160 | 5 | offset, 2, |
161 | 5 | (uint16_t)~teredoh->th_orgport); |
162 | 5 | } |
163 | 5 | offset += 2; |
164 | | |
165 | 5 | teredoh->th_iporgaddr = tvb_get_ipv4(tvb, offset); |
166 | 5 | if (tree) { |
167 | 5 | proto_tree_add_ipv4(tree, hf_teredo_orig_addr, tvb, |
168 | 5 | offset, 4, ~teredoh->th_iporgaddr); |
169 | 5 | } |
170 | 5 | offset += 4; |
171 | | |
172 | 5 | return offset; |
173 | 5 | } |
174 | | |
175 | | |
176 | | /* Determine if there is a sub-dissector and call it. This has been */ |
177 | | /* separated into a stand alone routine to other protocol dissectors */ |
178 | | /* can call to it, ie. socks */ |
179 | | |
180 | | |
181 | | static void |
182 | | decode_teredo_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,proto_tree *tree, int th_header) |
183 | 722 | { |
184 | 722 | tvbuff_t *next_tvb; |
185 | | |
186 | 722 | next_tvb = tvb_new_subset_remaining(tvb, offset); |
187 | | |
188 | 722 | if (dissector_try_uint(teredo_dissector_table, th_header, next_tvb, pinfo, tree)) |
189 | 0 | return; |
190 | | |
191 | 722 | call_dissector(data_handle,next_tvb, pinfo, tree); |
192 | 722 | } |
193 | | |
194 | | static int |
195 | | dissect_teredo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
196 | 724 | { |
197 | 724 | proto_tree *teredo_tree; |
198 | 724 | proto_item *ti; |
199 | 724 | int offset = 0; |
200 | 724 | static e_teredohdr teredohstruct[4], *teredoh; |
201 | 724 | static int teredoh_count = 0; |
202 | | |
203 | 724 | teredoh_count++; |
204 | 724 | if(teredoh_count>=4){ |
205 | 181 | teredoh_count=0; |
206 | 181 | } |
207 | 724 | teredoh = &teredohstruct[teredoh_count]; |
208 | | |
209 | 724 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Teredo"); |
210 | 724 | col_clear(pinfo->cinfo, COL_INFO); |
211 | | |
212 | 724 | ti = proto_tree_add_item(tree, proto_teredo, tvb, 0, -1, ENC_NA); |
213 | 724 | teredo_tree = proto_item_add_subtree(ti, ett_teredo); |
214 | | |
215 | 724 | teredoh->th_header = tvb_get_ntohs(tvb, offset); |
216 | | |
217 | 724 | if (teredoh->th_header == 1) { |
218 | 5 | offset = parse_teredo_auth(tvb, pinfo, teredo_tree, |
219 | 5 | offset, teredoh); |
220 | 5 | teredoh->th_header = tvb_get_ntohs(tvb, offset); |
221 | 5 | } |
222 | 719 | else |
223 | 719 | teredoh->th_indtyp = 0; |
224 | | |
225 | 724 | if ( teredoh->th_header == 0 ) { |
226 | 5 | offset = parse_teredo_orig(tvb, pinfo, teredo_tree, |
227 | 5 | offset, teredoh); |
228 | 5 | } |
229 | | |
230 | 724 | teredoh->th_ip_v_hl = tvb_get_uint8(tvb, offset); |
231 | | |
232 | 724 | decode_teredo_ports(tvb, offset, pinfo, tree, teredoh->th_header /* , teredoh->th_orgport*/); |
233 | 724 | tap_queue_packet(teredo_tap, pinfo, teredoh); |
234 | 724 | return tvb_captured_length(tvb); |
235 | 724 | } |
236 | | |
237 | | |
238 | | static bool |
239 | | dissect_teredo_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
240 | 0 | { |
241 | 0 | uint16_t val; |
242 | 0 | int offset = 0; |
243 | |
|
244 | 0 | if (tvb_captured_length_remaining(tvb, offset) < 40) |
245 | 0 | return false; |
246 | | |
247 | 0 | val = tvb_get_ntohs(tvb, offset); |
248 | |
|
249 | 0 | if (val == 1) /* possible auth header */ |
250 | 0 | { |
251 | 0 | uint8_t idlen, aulen; |
252 | |
|
253 | 0 | offset += 2; |
254 | |
|
255 | 0 | idlen = tvb_get_uint8(tvb, offset); |
256 | 0 | offset++; |
257 | |
|
258 | 0 | aulen = tvb_get_uint8(tvb, offset); |
259 | 0 | offset += 10; |
260 | |
|
261 | 0 | if (tvb_captured_length_remaining(tvb, offset) < idlen + aulen + 40) |
262 | 0 | return false; |
263 | | |
264 | 0 | offset += idlen + aulen; |
265 | |
|
266 | 0 | val = tvb_get_ntohs(tvb, offset); |
267 | 0 | } |
268 | | |
269 | 0 | if (val == 0) /* origin indication */ |
270 | 0 | { |
271 | 0 | offset += 8; |
272 | |
|
273 | 0 | if (tvb_captured_length_remaining(tvb, offset) < 40) |
274 | 0 | return false; |
275 | | |
276 | 0 | val = tvb_get_ntohs(tvb, offset); |
277 | 0 | } |
278 | | |
279 | | /* |
280 | | * We have to check upper-layer packet a little bit otherwise we will |
281 | | * match -almost- *ANY* packet. |
282 | | * These checks are in the Teredo specification by the way. |
283 | | * Unfortunately, that will cause false-negative if the snaplen is too |
284 | | * short to get the packet entirely. |
285 | | */ |
286 | 0 | if ((val >> 12) == 6) /* IPv6 header */ |
287 | 0 | { |
288 | | /* checks IPv6 payload length */ |
289 | 0 | val = tvb_get_ntohs(tvb, offset + 4); |
290 | 0 | offset += 40; |
291 | |
|
292 | 0 | if (val > 65467) |
293 | 0 | return false; /* length too big for Teredo */ |
294 | | |
295 | 0 | if (tvb_reported_length_remaining(tvb, offset) != val) |
296 | 0 | return false; /* length mismatch */ |
297 | | |
298 | 0 | dissect_teredo (tvb, pinfo, tree, data); |
299 | 0 | return true; |
300 | 0 | } |
301 | | |
302 | 0 | return false; /* not an IPv6 packet */ |
303 | 0 | } |
304 | | |
305 | | |
306 | | void |
307 | | proto_register_teredo(void) |
308 | 14 | { |
309 | 14 | static hf_register_info hf[] = { |
310 | | /* Authentication header */ |
311 | 14 | { &hf_teredo_auth, |
312 | 14 | { "Teredo Authentication header", "teredo.auth", |
313 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
314 | 14 | NULL, HFILL }}, |
315 | | |
316 | 14 | { &hf_teredo_auth_idlen, |
317 | 14 | { "Client identifier length", "teredo.auth.idlen", |
318 | 14 | FT_UINT8, BASE_DEC, NULL, 0x0, |
319 | 14 | "Client identifier length (ID-len)", HFILL }}, |
320 | | |
321 | 14 | { &hf_teredo_auth_aulen, |
322 | 14 | { "Authentication value length", "teredo.auth.aulen", |
323 | 14 | FT_UINT8, BASE_DEC, NULL, 0x0, |
324 | 14 | "Authentication value length (AU-len)", HFILL }}, |
325 | | |
326 | 14 | { &hf_teredo_auth_id, |
327 | 14 | { "Client identifier", "teredo.auth.id", |
328 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
329 | 14 | "Client identifier (ID)", HFILL }}, |
330 | | |
331 | 14 | { &hf_teredo_auth_value, |
332 | 14 | { "Authentication value", "teredo.auth.value", |
333 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
334 | 14 | "Authentication value (hash)", HFILL }}, |
335 | | |
336 | 14 | { &hf_teredo_auth_nonce, |
337 | 14 | { "Nonce value", "teredo.auth.nonce", |
338 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
339 | 14 | "Nonce value prevents spoofing Teredo server.", |
340 | 14 | HFILL }}, |
341 | | |
342 | 14 | { &hf_teredo_auth_conf, |
343 | 14 | { "Confirmation byte", "teredo.auth.conf", |
344 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
345 | 14 | "Confirmation byte is zero upon successful authentication.", |
346 | 14 | HFILL }}, |
347 | | |
348 | | /* Origin indication */ |
349 | 14 | { &hf_teredo_orig, |
350 | 14 | { "Teredo Origin Indication header", "teredo.orig", |
351 | 14 | FT_NONE, BASE_NONE, NULL, 0x0, |
352 | 14 | NULL, HFILL }}, |
353 | | |
354 | 14 | { &hf_teredo_orig_port, |
355 | 14 | { "Origin UDP port", "teredo.orig.port", |
356 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
357 | 14 | NULL, HFILL }}, |
358 | | |
359 | 14 | { &hf_teredo_orig_addr, |
360 | 14 | { "Origin IPv4 address", "teredo.orig.addr", |
361 | 14 | FT_IPv4, BASE_NONE, NULL, 0x0, |
362 | 14 | NULL, HFILL }}, |
363 | 14 | }; |
364 | | |
365 | 14 | static int *ett[] = { |
366 | 14 | &ett_teredo, &ett_teredo_auth, &ett_teredo_orig |
367 | 14 | }; |
368 | | |
369 | 14 | module_t *teredo_module; |
370 | | |
371 | 14 | proto_teredo = proto_register_protocol("Teredo IPv6 over UDP tunneling", "Teredo", "teredo"); |
372 | 14 | proto_register_field_array(proto_teredo, hf, array_length(hf)); |
373 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
374 | 14 | teredo_handle = register_dissector("teredo", dissect_teredo, proto_teredo); |
375 | | |
376 | | /* subdissector code */ |
377 | 14 | teredo_dissector_table = register_dissector_table("teredo", "Teredo", proto_teredo, FT_UINT16, BASE_DEC); |
378 | | |
379 | 14 | teredo_module = prefs_register_protocol(proto_teredo, NULL); |
380 | | |
381 | 14 | prefs_register_obsolete_preference(teredo_module, "heuristic_teredo"); |
382 | | |
383 | 14 | teredo_tap = register_tap("teredo"); |
384 | 14 | } |
385 | | |
386 | | void |
387 | | proto_reg_handoff_teredo(void) |
388 | 14 | { |
389 | 14 | data_handle = find_dissector("ipv6"); |
390 | | |
391 | 14 | dissector_add_uint_with_preference("udp.port", UDP_PORT_TEREDO, teredo_handle); |
392 | 14 | heur_dissector_add("udp", dissect_teredo_heur, "Teredo over UDP", "teredo_udp", proto_teredo, HEURISTIC_DISABLE); |
393 | 14 | } |
394 | | |
395 | | /* |
396 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
397 | | * |
398 | | * Local variables: |
399 | | * c-basic-offset: 8 |
400 | | * tab-width: 8 |
401 | | * indent-tabs-mode: t |
402 | | * End: |
403 | | * |
404 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
405 | | * :indentSize=8:tabSize=8:noTabs=false: |
406 | | */ |