Move various tables into the epan directory.
[obnox/wireshark/wip.git] / epan / dissectors / packet-ipdc.c
1 /* packet-ipdc.c
2  * Routines for IP Device Control (SS7 over IP) dissection
3  * Copyright Lucent Technologies 2004
4  * Josh Bailey <joshbailey@lucent.com> and Ruud Linders <ruud@lucent.com>
5  *
6  * Using IPDC spec 0.20.2
7  *
8  * $Id$
9  *
10  * Ethereal - Network traffic analyzer
11  * By Gerald Combs <gerald@ethereal.com>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  * 
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <glib.h>
38 #include <math.h>
39
40 #ifdef NEED_SNPRINTF_H
41 # include "snprintf.h"
42 #endif
43
44 #include <epan/packet.h>
45 #include "packet-ipdc.h"
46 #include "packet-tcp.h"
47 #include <epan/packet.h>
48 #include <epan/ipproto.h>
49 #include <epan/prefs.h>
50
51 static int proto_ipdc = -1;
52 static int hf_ipdc_nr = -1;
53 static int hf_ipdc_ns = -1;
54 static int hf_ipdc_payload_len = -1;
55 static int hf_ipdc_protocol_id = -1;
56 static int hf_ipdc_trans_id_size = -1;
57 static int hf_ipdc_trans_id = -1;
58 static int hf_ipdc_message_code = -1;
59
60 static gint ett_ipdc = -1;
61 static gint ett_ipdc_tag = -1;
62
63 static gboolean ipdc_desegment = TRUE;
64 static gint ipdc_port_pref = TCP_PORT_IPDC;
65 static gboolean new_packet = FALSE;
66
67 static dissector_handle_t q931_handle;
68
69 void proto_reg_handoff_ipdc(void);
70
71
72 static guint
73 get_ipdc_pdu_len(tvbuff_t *tvb, int offset)
74 {
75         return tvb_get_ntohs(tvb,offset+2)+4;
76 }
77
78 static void
79 dissect_ipdc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
80 {
81         proto_item *ti;
82         proto_tree *ipdc_tree;
83         proto_item *ipdc_tag;
84         proto_tree *tag_tree;
85         tvbuff_t *q931_tvb;
86
87         char *des;
88         char *enum_val;
89         char *tmp_str;
90         char tmp_tag_text[IPDC_STR_LEN + 1];
91         const value_string *val_ptr;
92         guint32 type;
93         guint len;
94         guint i;
95         guint status;
96         gshort tag;
97         guint32 tmp_tag;
98
99         gshort nr = tvb_get_guint8(tvb,0);
100         gshort ns = tvb_get_guint8(tvb,1);
101         guint16 payload_len = (guint16) get_ipdc_pdu_len(tvb,0);
102
103         gshort protocol_id;
104         gshort trans_id_size;
105         guint32 trans_id;
106         guint16 message_code;
107         guint16 offset;
108
109         /* display IPDC protocol ID */
110         if (check_col(pinfo->cinfo, COL_PROTOCOL))
111                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPDC");
112
113         /* short frame... */
114         if (payload_len < 4)
115                 return;
116
117         /* clear info column and display send/receive sequence numbers */
118         if (check_col(pinfo->cinfo, COL_INFO)) {
119                 if (new_packet == TRUE) {
120                         col_clear(pinfo->cinfo, COL_INFO);
121                         new_packet = FALSE;
122                 }
123                 col_append_fstr(pinfo->cinfo, COL_INFO, "r=%u s=%u ",
124                 nr, ns);
125         }
126
127         if (payload_len == 4) {
128                 if (!tree)
129                         return;
130
131                 ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
132                 ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
133                 proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
134                 proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
135                 proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb, 2, 2,
136                         payload_len);
137
138                 return;
139         }
140
141         /* IPDC tags present - display message code and trans. ID */
142         protocol_id = tvb_get_guint8(tvb,4);
143         trans_id_size = TRANS_ID_SIZE_IPDC;
144         trans_id = tvb_get_ntohl(tvb,6);
145         message_code = tvb_get_ntohs(tvb,6+trans_id_size);
146         offset = 6 + trans_id_size + 2; /* past message_code */
147
148         if (check_col(pinfo->cinfo, COL_INFO))
149                 col_append_fstr(pinfo->cinfo, COL_INFO,
150                         "TID=%x %s ",
151                         trans_id,
152                         val_to_str(message_code, message_code_vals,
153                         TEXT_UNDEFINED));
154
155         if (!tree)
156                 return;
157
158         ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
159         ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
160
161         proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
162         proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
163         proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb,
164                 2, 2, payload_len);
165
166         proto_tree_add_item(ipdc_tree, hf_ipdc_protocol_id, tvb,
167                 4, 1, protocol_id);
168         proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id_size, tvb,
169                 5, 1, trans_id_size);
170         proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id, tvb,
171                 6, trans_id_size, trans_id);
172         proto_tree_add_item(ipdc_tree, hf_ipdc_message_code, tvb,
173                 6 + trans_id_size + 1, 1, message_code);
174
175         ipdc_tag = proto_tree_add_text(ipdc_tree, tvb, offset,
176         payload_len - offset, "IPDC tags");
177         tag_tree = proto_item_add_subtree(ipdc_tag, ett_ipdc_tag);
178
179         /* iterate through tags. first byte is tag, second is length,
180            in bytes, following is tag data. tag of 0x0 should be
181            end of tags. */
182         for (;;) {
183                 tag = tvb_get_guint8(tvb, offset);
184
185                 if (tag == 0x0) {
186                         if (offset == payload_len - 1) {
187                                 proto_tree_add_text(tag_tree, tvb,
188                                 offset, 1, "end of tags");
189                         } else {
190                                 proto_tree_add_text(tag_tree, tvb,
191                                 offset, 1, "data trailing end of tags");
192                         }
193
194                         break;
195                 }
196
197                 len = tvb_get_guint8(tvb,offset+1);
198                 des = val_to_str(tag, tag_description, TEXT_UNDEFINED);
199                 /* lookup tag type */
200                 for (i = 0; (ipdc_tag_types[i].tag != tag &&
201                         ipdc_tag_types[i].type != IPDC_UNKNOWN); i++)
202                 ;
203                 type = ipdc_tag_types[i].type;
204
205                 tmp_tag = 0;
206
207                 switch (type) {
208                         /* simple IPDC_ASCII strings */
209                         case IPDC_ASCII:
210                                 tmp_str = tvb_memdup(tvb, offset + 2, len);
211                                 strncpy(tmp_tag_text, tmp_str, len);
212                                 tmp_tag_text[len] = 0;
213                                 free(tmp_str);
214                                 proto_tree_add_text(tag_tree, tvb, offset,
215                                 len + 2, "0x%2.2x: %s: %s", tag, des,
216                                 tmp_tag_text);
217                         break;
218
219                         /* unsigned integers, or bytes */
220                         case IPDC_UINT:
221                         case IPDC_BYTE:
222                                 for (i = 0; i < len; i++) 
223                                         tmp_tag += tvb_get_guint8(tvb,
224                                                 offset + 2 + i) * (guint32)
225                                                         pow(256, len - (i + 1));
226
227                                 if (len == 1)
228                                         enum_val =
229                                                 val_to_str(IPDC_TAG(tag) +
230                                                 tmp_tag,
231                                                 tag_enum_type, TEXT_UNDEFINED);
232
233                                 if (len == 1 &&
234                                         strcmp(enum_val, TEXT_UNDEFINED) != 0) {
235                                         proto_tree_add_text(tag_tree, tvb,
236                                                 offset, len + 2,
237                                                 "0x%2.2x: %s: %s",
238                                                 tag, des, enum_val);
239                                 } else {
240                                         proto_tree_add_text(tag_tree, tvb,
241                                                 offset, len + 2,
242                                                 "0x%2.2x: %s: %u",
243                                                 tag, des, tmp_tag);
244                                 }
245                         break;
246
247                         /* IP addresses */
248                         case IPDC_IPA:
249                                 switch (len) {
250                                         case 4:
251                                                 g_snprintf(tmp_tag_text,
252                                                 IPDC_STR_LEN,
253                                                 "%u.%u.%u.%u",
254                                                 tvb_get_guint8(tvb, offset + 2),
255                                                 tvb_get_guint8(tvb, offset + 3),
256                                                 tvb_get_guint8(tvb, offset + 4),
257                                                 tvb_get_guint8(tvb, offset + 5)
258                                                 );
259                                         break;
260
261                                         case 6:
262                                                 g_snprintf(tmp_tag_text,
263                                                 IPDC_STR_LEN,
264                                                 "%u.%u.%u.%u:%u",
265                                                 tvb_get_guint8(tvb, offset + 2),
266                                                 tvb_get_guint8(tvb, offset + 3),
267                                                 tvb_get_guint8(tvb, offset + 4),
268                                                 tvb_get_guint8(tvb, offset + 5),
269                                                 tvb_get_ntohs(tvb, offset + 6));
270                                         break;
271
272                                         default:
273                                                 g_snprintf(tmp_tag_text,
274                                                 IPDC_STR_LEN,
275                                                 "Invalid IP address length %u",
276                                                  len);
277                                 }
278
279                                 proto_tree_add_text(tag_tree, tvb,
280                                         offset, len + 2,
281                                         "0x%2.2x: %s: %s",
282                                         tag, des, tmp_tag_text);
283                         break;
284
285                         /* Line status arrays */
286                         case IPDC_LINESTATUS:
287                         case IPDC_CHANNELSTATUS:
288                                 proto_tree_add_text(tag_tree, tvb, offset,
289                                 len + 2, "0x%2.2x: %s", tag, des);
290                                 val_ptr = (type == IPDC_LINESTATUS) ?
291                                         line_status_vals : channel_status_vals;
292
293                                 for (i = 0; i < len; i++) {
294                                         status = tvb_get_guint8(tvb,offset+2+i);
295
296                                         proto_tree_add_text(tag_tree, tvb,
297                                                 offset + 2 + i, 1, 
298                                                 " %.2u: %.2x (%s)",
299                                                 i + 1, status,
300                                                 val_to_str(status,
301                                                 val_ptr,
302                                                 TEXT_UNDEFINED));
303                                 }
304                         break;
305
306                         case IPDC_Q931:
307                                 q931_tvb =
308                                         tvb_new_subset(tvb, offset+2, len, len);
309                                 call_dissector(q931_handle,q931_tvb,pinfo,tree);
310                         break;
311
312                         case IPDC_ENCTYPE:
313                                 proto_tree_add_text(tag_tree, tvb,
314                                         offset, len + 2,
315                                         "0x%2.2x: %s: %s",
316                                         tag, des, val_to_str(
317                                         tvb_get_guint8(tvb,offset+2),
318                                         encoding_type_vals,
319                                         TEXT_UNDEFINED));
320
321                                 if (len == 2) {
322                                         proto_tree_add_text(tag_tree, tvb,
323                                                 offset, len + 2,
324                                                 "0x%2.2x: %s: %u",
325                                                 tag, des,
326                                                 tvb_get_guint8(tvb,offset+3));
327                                 }
328                         break;
329                                         
330                         /* default */
331                         default:
332                                 proto_tree_add_text(tag_tree, tvb, offset,
333                                 len + 2, "0x%2.2x: %s", tag, des);
334                 } /* switch */
335
336                 offset += len + 2;
337         }
338 }
339
340 static void
341 dissect_ipdc_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
342 {
343         dissect_ipdc_common(tvb, pinfo, tree);
344 }
345
346 static void
347 dissect_ipdc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
348 {
349         new_packet = TRUE;
350         tcp_dissect_pdus(tvb, pinfo, tree, ipdc_desegment, 4,
351                 get_ipdc_pdu_len, dissect_ipdc_tcp_pdu);
352 }
353
354 void
355 proto_register_ipdc(void)
356 {                 
357
358         static hf_register_info hf[] = {
359                 { &hf_ipdc_nr,
360                         { "N(r)",       "ipdc.nr",
361                         FT_UINT8, BASE_DEC, NULL, 0x0,
362                         "Receive sequence number", HFILL }
363                 },
364
365                 { &hf_ipdc_ns,
366                         { "N(s)",       "ipdc.ns",
367                         FT_UINT8, BASE_DEC, NULL, 0x0, 
368                         "Transmit sequence number", HFILL }
369                 },
370
371                 { &hf_ipdc_payload_len,
372                         { "Payload length",     "ipdc.length",
373                         FT_UINT16, BASE_DEC, NULL, 0x0,
374                         "Payload length", HFILL }
375                 },
376
377                 { &hf_ipdc_protocol_id,
378                         { "Protocol ID",        "ipdc.protocol_id",
379                         FT_UINT8, BASE_HEX, NULL, 0x0,
380                         "Protocol ID", HFILL }
381                 },
382
383                 { &hf_ipdc_trans_id_size,
384                         { "Transaction ID size",        "ipdc.trans_id_size",
385                         FT_UINT8, BASE_DEC, NULL, 0x0,
386                         "Transaction ID size", HFILL }
387                 },
388
389                 { &hf_ipdc_trans_id,
390                         { "Transaction ID",     "ipdc.trans_id",
391                         FT_BYTES, BASE_HEX, NULL, 0x0,
392                         "Transaction ID", HFILL }
393                 },
394
395                 { &hf_ipdc_message_code,
396                         { "Message code",       "ipdc.message_code",
397                         FT_UINT16, BASE_HEX, VALS(message_code_vals), 0x0,
398                         "Message Code", HFILL }
399                 },
400         };
401
402         static gint *ett[] = {
403                 &ett_ipdc,
404                 &ett_ipdc_tag,
405         };
406
407         module_t *ipdc_module;
408
409         proto_ipdc = proto_register_protocol("IP Device Control (SS7 over IP)",
410             "IPDC", "ipdc");
411         proto_register_field_array(proto_ipdc, hf, array_length(hf));
412         proto_register_subtree_array(ett, array_length(ett));
413
414         ipdc_module = prefs_register_protocol(proto_ipdc, proto_reg_handoff_ipdc);
415         prefs_register_bool_preference(ipdc_module, "desegment_ipdc_messages",
416                 "Reassemble IPDC messages spanning multiple TCP segments",
417                 "Whether the IPDC dissector should reassemble messages spanning multiple TCP segments."
418                 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
419                 &ipdc_desegment);
420         prefs_register_uint_preference(ipdc_module, "tcp.port",
421                 "IPDC monitoring port",
422                 "Set the IPDC monitoring port", 10,
423                 &ipdc_port_pref);
424 }
425
426 void
427 proto_reg_handoff_ipdc(void)
428 {
429         static gint last_ipdc_port_pref = 0;
430         static dissector_handle_t ipdc_tcp_handle = NULL;
431
432         if (ipdc_tcp_handle) {
433                 dissector_delete("tcp.port", last_ipdc_port_pref,
434                         ipdc_tcp_handle);
435         } else {
436                 ipdc_tcp_handle = 
437                         create_dissector_handle(dissect_ipdc_tcp, proto_ipdc);
438                 q931_handle = find_dissector("q931");
439         }
440
441         last_ipdc_port_pref = ipdc_port_pref;
442         dissector_add("tcp.port", ipdc_port_pref, ipdc_tcp_handle);
443 }