From Marc Petit-Huguenin via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id...
[obnox/wireshark/wip.git] / epan / dissectors / packet-reload-framing.c
1 /* -*- Mode: C; tab-width: 2 -*- */
2 /* packet-reload-framing.c
3  * Routines for REsource LOcation And Discovery (RELOAD) Framing
4  * Author: Stephane Bryant <sbryant@glycon.org>
5  * Copyright 2010 Stonyfish Inc.
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26  *
27  * Please refer to the following specs for protocol detail:
28  * - draft-ietf-p2psip-base-13
29  */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <epan/conversation.h>
36 #include <epan/expert.h>
37 #include <packet-tcp.h>
38
39 /* Initialize the protocol and registered fields */
40 static int proto_reload_framing = -1;
41
42 static int hf_reload_framing_type = -1;
43 static int hf_reload_framing_sequence = -1;
44 static int hf_reload_framing_ack_sequence = -1;
45 static int hf_reload_framing_message = -1;
46 static int hf_reload_framing_message_length = -1;
47 static int hf_reload_framing_received = -1;
48 static int hf_reload_framing_duplicate = -1;
49 static int hf_reload_framing_response_in = -1;
50 static int hf_reload_framing_response_to = -1;
51 static int hf_reload_framing_time = -1;
52
53 static dissector_handle_t reload_handle;
54
55 /* Structure containing transaction specific information */
56 typedef struct _reload_frame_t {
57   guint32 data_frame;
58   guint32 ack_frame;
59   nstime_t req_time;
60 } reload_frame_t;
61
62 /* Structure containing conversation specific information */
63 typedef struct _reload_frame_conv_info_t {
64   emem_tree_t *transaction_pdus;
65 } reload_conv_info_t;
66
67
68 /* RELOAD Message classes = (message_code & 0x1) (response = request +1) */
69 #define DATA            128
70 #define ACK             129
71
72
73 /* Initialize the subtree pointers */
74 static gint ett_reload_framing = -1;
75 static gint ett_reload_framing_message = -1;
76
77
78 #define UDP_PORT_RELOAD                 6084
79 #define TCP_PORT_RELOAD                 6084
80
81 #define MIN_HDR_LENGTH                             9
82 #define MIN_RELOADDATA_HDR_LENGTH                  38
83
84 #define RELOAD_TOKEN                    0xd2454c4f
85
86 static const value_string types[] = {
87   {DATA, "DATA"},
88   {ACK,  "ACK"},
89   {0x00, NULL}
90 };
91
92 static guint
93 get_reload_framing_message_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
94 {
95   /* Get the type */
96   guint32 length = 9;
97
98
99   if (tvb_get_guint8(tvb, offset) == DATA) {
100
101     length = 1 + 4;
102     length += 3;
103     length += (tvb_get_ntohs(tvb, 1 + 4)<<8)+ tvb_get_guint8(tvb, 1 + 4 + 2);
104   }
105
106   return length;
107 }
108
109
110 static int
111 dissect_reload_framing_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
112 {
113   proto_item *ti;
114   proto_tree *reload_framing_tree;
115   guint32 relo_token;
116   guint32 message_length=0;
117   guint32 sequence;
118   guint effective_length;
119   guint16 offset;
120   conversation_t *conversation;
121   reload_conv_info_t *reload_framing_info;
122   reload_frame_t * reload_frame;
123   guint8 type;
124
125   offset = 0;
126   effective_length = tvb_length(tvb);
127
128   /* First, make sure we have enough data to do the check. */
129   if (effective_length < MIN_HDR_LENGTH)
130     return 0;
131
132   /* Get the type
133    * http://tools.ietf.org/html/draft-ietf-p2psip-base-12
134    * 5.6.2.  Framing Header
135    */
136   type = tvb_get_guint8(tvb, 0);
137
138   switch(type){
139     case DATA:
140       /* in the data type, check the reload token to be sure this
141       *  is a reLoad packet
142       */
143       message_length = (tvb_get_ntohs(tvb, 1 + 4)<<8)+ tvb_get_guint8(tvb, 1 + 4 + 2);
144       if (message_length < MIN_RELOADDATA_HDR_LENGTH) {
145         return 0;
146       }
147       relo_token = tvb_get_ntohl(tvb,1 + 4 + 3);
148       if (relo_token != RELOAD_TOKEN) {
149         return 0;
150       }
151       break;
152     case ACK:
153       if (effective_length != 9) {
154         return 0;
155       }
156       break;
157     default:
158       return 0;
159   }
160
161
162   /* The message seems to be a valid RELOAD framing message! */
163
164   col_set_str(pinfo->cinfo, COL_PROTOCOL, "RELOAD Frame");
165   col_clear(pinfo->cinfo, COL_INFO);
166
167   /* Create the transaction key which may be used to track the conversation */
168   sequence = tvb_get_ntohl(tvb, 1);
169
170   conversation = find_or_create_conversation(pinfo);
171
172   /*
173    * Do we already have a state structure for this conv
174    */
175   reload_framing_info = conversation_get_proto_data(conversation, proto_reload_framing);
176   if (!reload_framing_info) {
177     /* No.  Attach that information to the conversation, and add
178      * it to the list of information structures.
179      */
180     reload_framing_info = se_alloc(sizeof(reload_conv_info_t));
181     reload_framing_info->transaction_pdus = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "reload_framing_transaction_pdus");
182     conversation_add_proto_data(conversation, proto_reload_framing, reload_framing_info);
183   }
184
185   if (!pinfo->fd->flags.visited) {
186     if ((reload_frame =
187          se_tree_lookup32(reload_framing_info->transaction_pdus, sequence)) == NULL) {
188       reload_frame = se_alloc(sizeof(reload_frame_t));
189       reload_frame->data_frame = 0;
190       reload_frame->ack_frame = 0;
191       reload_frame->req_time = pinfo->fd->abs_ts;
192       se_tree_insert32(reload_framing_info->transaction_pdus, sequence, (void *)reload_frame);
193     }
194
195     /* check whether the message is a request or a response */
196
197     if (type == DATA) {
198       /* This is a data */
199       if (reload_frame->data_frame == 0) {
200         reload_frame->data_frame = pinfo->fd->num;
201       }
202     }
203     else {
204       /* This is a catch-all for all non-request messages */
205       if (reload_frame->ack_frame == 0) {
206         reload_frame->ack_frame = pinfo->fd->num;
207       }
208     }
209   }
210   else {
211     reload_frame=se_tree_lookup32(reload_framing_info->transaction_pdus, sequence);
212   }
213
214   if (!reload_frame) {
215     /* create a "fake" pana_trans structure */
216     reload_frame = ep_alloc(sizeof(reload_frame_t));
217     reload_frame->data_frame = 0;
218     reload_frame->ack_frame = 0;
219     reload_frame->req_time = pinfo->fd->abs_ts;
220   }
221
222   ti = proto_tree_add_item(tree, proto_reload_framing, tvb, 0, -1, FALSE);
223
224   reload_framing_tree = proto_item_add_subtree(ti, ett_reload_framing);
225
226   col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(type, types, "Unknown"));
227   proto_item_append_text(ti, ": %s", val_to_str(type, types, "Unknown"));
228
229   /* Retransmission control */
230   if (type == DATA) {
231     if (reload_frame->data_frame != pinfo->fd->num) {
232       proto_item *it;
233       it = proto_tree_add_uint(reload_framing_tree, hf_reload_framing_duplicate, tvb, 0, 0, reload_frame->data_frame);
234       PROTO_ITEM_SET_GENERATED(it);
235     }
236     if (reload_frame->ack_frame) {
237       proto_item *it;
238       it = proto_tree_add_uint(reload_framing_tree, hf_reload_framing_response_in, tvb, 0, 0, reload_frame->ack_frame);
239       PROTO_ITEM_SET_GENERATED(it);
240     }
241   }
242   else {
243     /* This is a response */
244     if (reload_frame->ack_frame != pinfo->fd->num) {
245       proto_item *it;
246       it = proto_tree_add_uint(reload_framing_tree, hf_reload_framing_duplicate, tvb, 0, 0, reload_frame->ack_frame);
247       PROTO_ITEM_SET_GENERATED(it);
248     }
249
250     if (reload_frame->data_frame) {
251       proto_item *it;
252       nstime_t ns;
253
254       it = proto_tree_add_uint(reload_framing_tree, hf_reload_framing_response_to, tvb, 0, 0, reload_frame->data_frame);
255       PROTO_ITEM_SET_GENERATED(it);
256
257       nstime_delta(&ns, &pinfo->fd->abs_ts, &reload_frame->req_time);
258       it = proto_tree_add_time(reload_framing_tree, hf_reload_framing_time, tvb, 0, 0, &ns);
259       PROTO_ITEM_SET_GENERATED(it);
260     }
261   }
262
263   /*
264    * Message dissection
265    */
266   proto_tree_add_item(reload_framing_tree, hf_reload_framing_type, tvb, offset , 1, FALSE);
267   offset += 1;
268   switch (type) {
269
270   case DATA:
271     {
272       proto_item *ti_message;
273       proto_tree *message_tree;
274       tvbuff_t *next_tvb;
275
276       proto_tree_add_item(reload_framing_tree, hf_reload_framing_sequence, tvb, offset , 4, FALSE);
277       offset += 4;
278       ti_message = proto_tree_add_item(reload_framing_tree, hf_reload_framing_message, tvb, offset, 3 + message_length, FALSE);
279       message_tree = proto_item_add_subtree(ti_message, ett_reload_framing_message);
280       proto_tree_add_item(message_tree, hf_reload_framing_message_length, tvb, offset, 3, FALSE);
281       offset += 3;
282       next_tvb = tvb_new_subset(tvb, offset, effective_length - offset, message_length);
283       if (reload_handle == NULL) {
284         expert_add_info_format(pinfo, ti, PI_PROTOCOL, PI_WARN, "Can not find reload dissector");
285         return tvb_length(tvb);
286       }
287       call_dissector_only(reload_handle, next_tvb, pinfo, message_tree);
288     }
289     break;
290
291   case ACK:
292     proto_tree_add_item(reload_framing_tree, hf_reload_framing_ack_sequence, tvb, offset , 4, FALSE);
293     offset += 4;
294     proto_tree_add_item(reload_framing_tree, hf_reload_framing_received, tvb, offset , 4, FALSE);
295     break;
296
297   default:
298     DISSECTOR_ASSERT_NOT_REACHED();
299   }
300
301   return tvb_length(tvb);
302 }
303
304 static int
305 dissect_reload_framing_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
306 {
307   return dissect_reload_framing_message(tvb, pinfo, tree);
308 }
309
310 static void
311 dissect_reload_framing_message_no_return(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
312 {
313   dissect_reload_framing_message(tvb, pinfo, tree);
314 }
315
316 static void
317 dissect_reload_framing_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
318 {
319   /* XXX: Check if we have a valid RELOAD Frame Type ? */
320   tcp_dissect_pdus(tvb, pinfo, tree, TRUE, MIN_HDR_LENGTH,
321                    get_reload_framing_message_length, dissect_reload_framing_message_no_return);
322 }
323
324 static gboolean
325 dissect_reload_framing_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
326 {
327   if (dissect_reload_framing_message(tvb, pinfo, tree) == 0) {
328     /*
329      * It wasn't a valid RELOAD message, and wasn't
330      * dissected as such.
331      */
332     return FALSE;
333   }
334   return TRUE;
335 }
336
337 void
338 proto_register_reload_framing(void)
339 {
340
341   static hf_register_info hf[] = {
342     { &hf_reload_framing_type,
343       { "Framed Message Type", "reload_framing.type", FT_UINT8,
344         BASE_DEC, VALS(types),  0x0,  NULL, HFILL }
345     },
346     { &hf_reload_framing_sequence,
347       { "Sequence", "reload_framing.sequence", FT_UINT32,
348         BASE_DEC, NULL, 0x0,  NULL, HFILL }
349     },
350     { &hf_reload_framing_ack_sequence,
351       { "ACK Sequence", "reload_framing.ack_sequence", FT_UINT32,
352         BASE_DEC, NULL, 0x0,  NULL, HFILL }
353     },
354     { &hf_reload_framing_message,
355       { "Message", "reload_framing.message", FT_BYTES,
356         BASE_NONE, NULL, 0x0,  NULL, HFILL }
357     },
358     { &hf_reload_framing_message_length,
359       { "Message length", "reload_framing.message.length", FT_UINT32,
360         BASE_DEC, NULL, 0x0,  NULL, HFILL }
361     },
362     { &hf_reload_framing_received,
363       { "Received", "reload_framing.received", FT_UINT32,
364         BASE_HEX, NULL, 0x0,  NULL, HFILL }
365     },
366     { &hf_reload_framing_response_in,
367       { "Response In",  "reload_framing.response-in", FT_FRAMENUM,
368         BASE_NONE, NULL, 0x0, "The response to this RELOAD Request is in this frame", HFILL }
369     },
370     { &hf_reload_framing_response_to,
371       { "Request In", "reload_framing.response-to", FT_FRAMENUM,
372         BASE_NONE, NULL, 0x0, "This is a response to the RELOAD Request in this frame", HFILL }
373     },
374     { &hf_reload_framing_time,
375       { "Time", "reload_framing.time", FT_RELATIVE_TIME,
376         BASE_NONE, NULL, 0x0, "The time between the Request and the Response", HFILL }
377     },
378     { &hf_reload_framing_duplicate,
379       { "Duplicated original message in", "reload_framing.duplicate", FT_FRAMENUM,
380         BASE_NONE, NULL, 0x0, "This is a duplicate of RELOAD message in this frame", HFILL }
381     },
382   };
383
384   /* Setup protocol subtree array */
385   static gint *ett[] = {
386     &ett_reload_framing,
387     &ett_reload_framing_message,
388   };
389
390   /* Register the protocol name and description */
391   proto_reload_framing = proto_register_protocol("REsource LOcation And Discovery Framing", "RELOAD FRAMING", "reload-framing");
392
393   /* Required function calls to register the header fields and subtrees used */
394   proto_register_field_array(proto_reload_framing, hf, array_length(hf));
395   proto_register_subtree_array(ett, array_length(ett));
396
397   register_dissector("reload-framing", dissect_reload_framing_message_no_return, proto_reload_framing);
398
399 }
400
401 void
402 proto_reg_handoff_reload_framing(void)
403 {
404
405   dissector_handle_t reload_framing_tcp_handle;
406   dissector_handle_t reload_framing_udp_handle;
407
408   reload_framing_tcp_handle = create_dissector_handle(dissect_reload_framing_tcp, proto_reload_framing);
409   reload_framing_udp_handle = new_create_dissector_handle(dissect_reload_framing_udp, proto_reload_framing);
410
411   reload_handle = find_dissector("reload");
412
413   dissector_add_uint("tcp.port", TCP_PORT_RELOAD, reload_framing_tcp_handle);
414   dissector_add_uint("udp.port", UDP_PORT_RELOAD, reload_framing_udp_handle);
415
416   heur_dissector_add("udp", dissect_reload_framing_heur, proto_reload_framing);
417   heur_dissector_add("tcp", dissect_reload_framing_heur, proto_reload_framing);
418 }
419