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