From Didier Gautheron:
[obnox/wireshark/wip.git] / epan / dissectors / packet-pw-cesopsn.c
1 /* packet-pw-satop.c
2  * Routines for CESoPSN PW dissection as per RFC5086.
3  * Copyright 2009, Dmitry Trebich, Artem Tamazov <artem.tamazov@tellabs.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  *
25  * History:
26  * ---------------------------------
27  * 16.03.2009 initial implementation for MPLS
28  * 14.08.2009 added: support for IP/UDP demultiplexing
29  * Not supported yet:
30  * - All PW modes, except Basic NxDS0 mode.
31  * - <Optional> RTP Headers (RFC3550)
32  * - Decoding of PW payload
33  */
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <glib.h>
43 #include <epan/packet.h>
44 #include <epan/expert.h>
45
46 #include "packet-mpls.h"
47 #include "packet-pw-common.h"
48
49 static gint proto = -1;
50 static gint ett = -1;
51
52 static int hf_cw = -1;
53 static int hf_cw_bits03 = -1;
54 static int hf_cw_lm = -1;
55 static int hf_cw_r = -1;
56 static int hf_cw_frg = -1;
57 static int hf_cw_len = -1;
58 static int hf_cw_seq = -1;
59 static int hf_payload = -1;
60 static int hf_payload_l = -1;
61
62 static dissector_handle_t data_handle;
63 static dissector_handle_t pw_padding_handle;
64
65 const char pwc_longname_pw_cesopsn[] = "CESoPSN basic NxDS0 mode (no RTP support)";
66 static const char shortname[] = "CESoPSN basic (no RTP)";
67
68 static const value_string vals_cw_lm[] = {
69         /* note that bitmask in hs_register_info is 0xb == 1011B */
70         /* this is why 0x8 comes just after 0x3 */
71         { 0x0,  "Normal situation - no AC faults" },
72         /*{ 0x1,        "Reserved combination" },*/
73         { 0x2,  "AC Fault - RDI condition" },
74         { 0x3,  "Reserved for CESoPSN signaling" },
75         { 0x8,  "AC Fault - TDM data is invalid" },
76         /*{ 0x9,        "Reserved combination" },*/
77         /*{ 0xa,        "Reserved combination" },*/
78         /*{ 0xb,        "Reserved combination" },*/
79         { 0,    NULL }
80 };
81
82
83 static
84 void dissect_pw_cesopsn( tvbuff_t * tvb_original
85                                                 ,packet_info * pinfo
86                                                 ,proto_tree * tree
87                                                 ,pwc_demux_type_t demux)
88 {
89         const int encaps_size = 4; /*RTP header in encapsulation is not supported yet*/
90         gint packet_size;
91         gint payload_size;
92         gint padding_size;
93         pwc_packet_properties_t properties;
94
95         packet_size = tvb_reported_length_remaining(tvb_original, 0);
96
97         /*
98          * FIXME
99          * "4" below should be replaced by something like "min_packet_size_this_dissector"
100          * Also call to dissect_try_cw_first_nibble() should be moved before this block
101          */
102         if (packet_size < 4) /* 4 is smallest size which may be sensible (for PWACH dissector) */
103         {
104                 if (tree)
105                 {
106                         proto_item  *item;
107                         item = proto_tree_add_item(tree, proto, tvb_original, 0, -1, FALSE);
108                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
109                                 "PW packet size (%d) is too small to carry sensible information"
110                                 ,(int)packet_size);
111                 }
112                 if (check_col(pinfo->cinfo, COL_PROTOCOL))
113                 {
114                         col_set_str(pinfo->cinfo, COL_PROTOCOL, shortname);
115                 }
116                 col_set_str(pinfo->cinfo, COL_INFO, "Malformed: PW packet is too small");
117                 return;
118         }
119
120         switch (demux)
121         {
122         case PWC_DEMUX_MPLS:
123                 if (dissect_try_cw_first_nibble(tvb_original, pinfo, tree))
124                 {
125                         return;
126                 }
127                 break;
128         case PWC_DEMUX_UDP:
129                 break;
130         default:
131                 DISSECTOR_ASSERT_NOT_REACHED();
132                 return;
133         }
134
135         /* check how "good" is this packet */
136         /* also decide payload length from packet size and CW */
137         properties = PWC_PACKET_PROPERTIES_T_INITIALIZER;
138         if (0 != (tvb_get_guint8(tvb_original, 0) & 0xf0 /*bits03*/))
139         {
140                 properties |= PWC_CW_BAD_BITS03;
141         }
142         if (0 != (tvb_get_guint8(tvb_original, 1) & 0xc0 /*frag*/))
143         {
144                 properties |= PWC_CW_BAD_FRAG;
145         }
146         {
147                 /* RFC5086:
148                  * [LEN (bits (10 to 15) MAY be used to carry the length of the CESoPSN
149                  * packet (defined as the size of the CESoPSN header + the payload size)
150                  * if it is less than 64 bytes, and MUST be set to zero otherwise.
151                  * Note:  If fixed RTP header is used in the encapsulation, it is
152                  * considered part of the CESoPSN header.]
153                  *
154                  * Note that this differs from RFC4385's definition of length:
155                  * [ If the MPLS payload is less than 64 bytes, the length field
156                  * MUST be set to the length of the PW payload...]
157                  *
158                  * We will use RFC5086's definition here.
159                  */
160                 int cw_len;
161                 gint payload_size_from_packet;
162
163                 cw_len = tvb_get_guint8(tvb_original, 1) & 0x3f;
164                 payload_size_from_packet = packet_size - encaps_size;
165                 if (cw_len != 0)
166                 {
167                         gint payload_size_from_cw;
168                         payload_size_from_cw = cw_len - encaps_size;
169                         /*
170                          * Assumptions for error case,
171                          * will be overwritten if no errors found:
172                          */
173                         payload_size = payload_size_from_packet;
174                         padding_size = 0;
175
176                         if (payload_size_from_cw < 0)
177                         {
178                                 properties |= PWC_CW_BAD_PAYLEN_LT_0;
179                         }
180                         else if (payload_size_from_cw > payload_size_from_packet)
181                         {
182                                 properties |= PWC_CW_BAD_PAYLEN_GT_PACKET;
183                         }
184                         else if (payload_size_from_packet >= 64)
185                         {
186                                 properties |= PWC_CW_BAD_LEN_MUST_BE_0;
187                         }
188                         else /* ok */
189                         {
190                                 payload_size = payload_size_from_cw;
191                                 padding_size = payload_size_from_packet - payload_size_from_cw; /* >=0 */
192                         }
193                 }
194                 else
195                 {
196                         payload_size = payload_size_from_packet;
197                         padding_size = 0;
198                 }
199         }
200
201         {
202                 guint8 cw_lm;
203                 cw_lm = tvb_get_guint8(tvb_original, 0) & 0x0b /*l+mod*/;
204                 if (NULL == match_strval(cw_lm, vals_cw_lm))
205                 {
206                         properties |= PWC_CW_SUSPECT_LM;
207                 }
208
209                 {
210                         guint8 l_bit, m_bits;
211                         l_bit  = (cw_lm & 0x08) >> 3;
212                         m_bits = (cw_lm & 0x03) >> 0;
213                         if ((l_bit == 0 && m_bits == 0x0) /*CESoPSN data packet - normal situation*/
214                             ||(l_bit == 0 && m_bits == 0x2) /*CESoPSN data packet - RDI on the AC*/ )
215                         {
216                                 if ((payload_size == 0) || ((payload_size % 8) != 0))
217                                 {
218                                         properties |= PWC_PAY_SIZE_BAD;
219                                 }
220                         }
221                         else if (l_bit == 1 && m_bits == 0x0) /*TDM data is invalid; payload MAY be omitted*/
222                         {
223                                 /*allow any size of payload*/
224                         }
225                         else /*reserved combinations*/
226                         {
227                                 /*allow any size of payload*/
228                         }
229                 }
230         }
231
232         /* fill up columns*/
233         if (check_col(pinfo->cinfo, COL_PROTOCOL))
234         {
235                 col_set_str(pinfo->cinfo, COL_PROTOCOL, shortname);
236         }
237         if (check_col(pinfo->cinfo, COL_INFO))
238         {
239                 col_clear(pinfo->cinfo, COL_INFO);
240                 if (properties & PWC_ANYOF_CW_BAD)
241                 {
242                         col_append_str(pinfo->cinfo, COL_INFO, "CW:Bad, ");
243                 }
244                 else if (properties & PWC_ANYOF_CW_SUSPECT)
245                 {
246                         col_append_str(pinfo->cinfo, COL_INFO, "CW:Suspect, ");
247                 }
248
249                 if (properties & PWC_PAY_SIZE_BAD)
250                 {
251                         col_append_str(pinfo->cinfo, COL_INFO, "Payload size:Bad, ");
252                 }
253
254                 col_append_fstr(pinfo->cinfo, COL_INFO, "TDM octets:%d", (int)payload_size);
255
256                 if (padding_size != 0)
257                 {
258                         col_append_fstr(pinfo->cinfo, COL_INFO, ", Padding:%d", (int)padding_size);
259                 }
260         }
261
262         {
263                 proto_item* item;
264                 item = proto_tree_add_item(tree, proto, tvb_original, 0, -1, FALSE);
265                 pwc_item_append_cw(item,tvb_get_ntohl(tvb_original, 0),TRUE);
266                 pwc_item_append_text_n_items(item,(int)payload_size,"octet");
267                 {
268                         proto_tree* tree;
269                         tree = proto_item_add_subtree(item, ett);
270                         {
271                                 tvbuff_t* tvb;
272                                 proto_item* item;
273                                 tvb = tvb_new_subset(tvb_original, 0, PWC_SIZEOF_CW, PWC_SIZEOF_CW);
274                                 item = proto_tree_add_item(tree, hf_cw, tvb, 0, -1, FALSE);
275                                 pwc_item_append_cw(item,tvb_get_ntohl(tvb, 0),FALSE);
276                                 {
277                                         proto_tree* tree;
278                                         tree = proto_item_add_subtree(item, ett);
279                                         {
280                                                 proto_item* item;
281                                                 if (properties & PWC_CW_BAD_BITS03) /*display only if value is wrong*/
282                                                 {
283                                                         item = proto_tree_add_item(tree, hf_cw_bits03, tvb, 0, 1, FALSE);
284                                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
285                                                                 ,"Bits 0..3 of Control Word must be 0");
286                                                 }
287
288                                                 item = proto_tree_add_item(tree, hf_cw_lm,  tvb, 0, 1, FALSE);
289                                                 if (properties & PWC_CW_SUSPECT_LM)
290                                                 {
291                                                         expert_add_info_format(pinfo, item, PI_UNDECODED, PI_WARN
292                                                                 ,"Reserved combination of L and Modifier bits");
293                                                 }
294
295                                                 (void)proto_tree_add_item(tree, hf_cw_r, tvb, 0, 1, FALSE);
296
297                                                 item = proto_tree_add_item(tree, hf_cw_frg, tvb, 1, 1, FALSE);
298                                                 if (properties & PWC_CW_BAD_FRAG)
299                                                 {
300                                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
301                                                                 ,"Fragmentation of payload is not allowed"
302                                                                 " for basic CESoPSN mode");
303                                                 }
304
305                                                 item = proto_tree_add_item(tree, hf_cw_len, tvb, 1, 1, FALSE);
306                                                 if (properties & PWC_CW_BAD_PAYLEN_LT_0)
307                                                 {
308                                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
309                                                                 ,"Bad Length: too small, must be > %d"
310                                                                 ,(int)encaps_size);
311                                                 }
312                                                 if (properties & PWC_CW_BAD_PAYLEN_GT_PACKET)
313                                                 {
314                                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
315                                                                 ,"Bad Length: must be <= than PSN packet size (%d)"
316                                                                 ,(int)packet_size);
317                                                 }
318                                                 if (properties & PWC_CW_BAD_LEN_MUST_BE_0)
319                                                 {
320                                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
321                                                                 ,"Bad Length: must be 0 if CESoPSN packet size (%d) is > 64"
322                                                                 ,(int)packet_size);
323                                                 }
324
325                                                 proto_tree_add_item(tree, hf_cw_seq, tvb, 2, 2, FALSE);
326
327                                         }
328                                 }
329                         }
330                 }
331
332                 /* payload */
333                 if (payload_size == 0)
334                 {
335                         if (properties & PWC_PAY_SIZE_BAD)
336                         {
337                                 expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
338                                         ,"CESoPSN payload: none found. Size of payload must be <> 0");
339                         }
340                         else
341                         {
342                                 expert_add_info_format(pinfo, item, PI_UNDECODED, PI_NOTE
343                                         ,"CESoPSN payload: omitted to conserve bandwidth");
344                         }
345                 }
346                 else
347                 {
348                         proto_tree* tree;
349                         tree = proto_item_add_subtree(item, ett);
350                         {
351                                 proto_item* item;
352                                 tvbuff_t* tvb;
353                                 tvb = tvb_new_subset(tvb_original, PWC_SIZEOF_CW, payload_size, payload_size);
354                                 item = proto_tree_add_item(tree, hf_payload, tvb, 0, -1, FALSE);
355                                 pwc_item_append_text_n_items(item,(int)payload_size,"octet");
356                                 if (properties & PWC_PAY_SIZE_BAD)
357                                 {
358                                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
359                                                 ,"CESoPSN packet payload size must be multiple of 8");
360                                 }
361                                 tree = proto_item_add_subtree(item, ett);
362                                 call_dissector(data_handle, tvb, pinfo, tree);
363                                 item = proto_tree_add_int(tree, hf_payload_l, tvb, 0, 0
364                                         ,(int)payload_size); /* allow filtering */
365                                 PROTO_ITEM_SET_HIDDEN(item);
366                         }
367                 }
368
369                 /* padding */
370                 if (padding_size > 0)
371                 {
372                         proto_tree* tree;
373                         tree = proto_item_add_subtree(item, ett);
374                         {
375                                 tvbuff_t* tvb;
376                                 tvb = tvb_new_subset(tvb_original, PWC_SIZEOF_CW + payload_size, padding_size, -1);
377                                 call_dissector(pw_padding_handle, tvb, pinfo, tree);
378                         }
379                 }
380         }
381         return;
382 }
383
384
385 static
386 void dissect_pw_cesopsn_mpls( tvbuff_t * tvb_original, packet_info * pinfo, proto_tree * tree)
387 {
388         dissect_pw_cesopsn(tvb_original,pinfo,tree,PWC_DEMUX_MPLS);
389         return;
390 }
391
392
393 static
394 void dissect_pw_cesopsn_udp( tvbuff_t * tvb_original, packet_info * pinfo, proto_tree * tree)
395 {
396         dissect_pw_cesopsn(tvb_original,pinfo,tree,PWC_DEMUX_UDP);
397         return;
398 }
399
400
401 void proto_register_pw_cesopsn(void)
402 {
403         static hf_register_info hf[] = {
404                 { &hf_cw        ,{"Control Word"                ,""
405                                 ,FT_NONE                        ,BASE_NONE              ,NULL
406                                 ,0                              ,NULL                   ,HFILL }}
407                 ,{&hf_cw_bits03,{"Bits 0 to 3"                  ,"pwcesopsn.cw.bits03"
408                                 ,FT_UINT8                       ,BASE_DEC               ,NULL
409                                 ,0xf0                           ,NULL                   ,HFILL }}
410                 ,{ &hf_cw_lm,   {"L+M bits"                     ,"pwcesopsn.cw.lm"
411                                 ,FT_UINT8                       ,BASE_HEX               ,VALS(vals_cw_lm)
412                                 ,0x0b                           ,NULL                   ,HFILL }}
413                 ,{&hf_cw_r,     {"R bit: Local CE-bound IWF"    ,"pwcesopsn.cw.rbit"
414                                 ,FT_UINT8                       ,BASE_DEC               ,VALS(pwc_vals_cw_r_bit)
415                                 ,0x04                           ,NULL                   ,HFILL }}
416                 ,{&hf_cw_frg,   {"Fragmentation"                ,"pwcesopsn.cw.frag"
417                                 ,FT_UINT8                       ,BASE_DEC               ,VALS(pwc_vals_cw_frag)
418                                 ,0xc0                           ,NULL                   ,HFILL }}
419                 ,{&hf_cw_len,   {"Length"                       ,"pwcesopsn.cw.length"
420                                 ,FT_UINT8                       ,BASE_DEC               ,NULL
421                                 ,0x3f                           ,NULL                   ,HFILL }}
422                 ,{&hf_cw_seq,   {"Sequence number"              ,"pwcesopsn.cw.seqno"
423                                 ,FT_UINT16                      ,BASE_DEC               ,NULL
424                                 ,0                              ,NULL                   ,HFILL }}
425                 ,{&hf_payload   ,{"TDM payload"                 ,"pwcesopsn.payload"
426                                 ,FT_BYTES                       ,BASE_NONE              ,NULL
427                                 ,0                              ,NULL                   ,HFILL }}
428                 ,{&hf_payload_l ,{"TDM payload length"          ,"pwcesopsn.payload.len"
429                                 ,FT_INT32                       ,BASE_DEC               ,NULL
430                                 ,0                              ,NULL                   ,HFILL }}
431         };
432
433         static gint *ett_array[] = {
434                 &ett
435         };
436
437         proto = proto_register_protocol(pwc_longname_pw_cesopsn, shortname, "pwcesopsn");
438         proto_register_field_array(proto, hf, array_length(hf));
439         proto_register_subtree_array(ett_array, array_length(ett_array));
440         register_dissector("pw_cesopsn_mpls", dissect_pw_cesopsn_mpls, proto);
441         register_dissector("pw_cesopsn_udp", dissect_pw_cesopsn_udp, proto);
442         return;
443 }
444
445
446 void proto_reg_handoff_pw_cesopsn(void)
447 {
448         data_handle = find_dissector("data");
449         pw_padding_handle = find_dissector("pw_padding");
450         dissector_add("mpls.label", LABEL_INVALID, find_dissector("pw_cesopsn_mpls"));
451         dissector_add_handle("udp.port", find_dissector("pw_cesopsn_udp")); /* For Decode-As */
452         return;
453 }