Fix for bug 5422:
[obnox/wireshark/wip.git] / epan / dissectors / packet-pw-eth.c
1 /* packet-pw-eth.c
2  * Routines for ethernet PW dissection: it should be conform to RFC 4448.
3  *
4  * Copyright 2008 _FF_
5  *
6  * Francesco Fondelli <francesco dot fondelli, gmail dot com>
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 <stdlib.h>
34 #include <glib.h>
35 #include <epan/packet.h>
36 #include <epan/addr_resolv.h>
37
38 #include "packet-mpls.h"
39
40 static gint proto_pw_eth_cw = -1;
41 static gint proto_pw_eth_nocw = -1;
42 static gint proto_pw_eth_heuristic = -1;
43
44 static gint ett_pw_eth = -1;
45
46 static int hf_pw_eth = -1;
47 static int hf_pw_eth_cw = -1;
48 static int hf_pw_eth_cw_sequence_number = -1;
49
50 static dissector_handle_t eth_withoutfcs_handle;
51 static dissector_handle_t pw_eth_handle_cw;
52 static dissector_handle_t pw_eth_handle_nocw;
53
54 static void
55 dissect_pw_eth_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
56 {
57         proto_tree *pw_eth_tree = NULL;
58         proto_item *ti = NULL;
59         tvbuff_t *next_tvb = NULL;
60         guint16 sequence_number = 0;
61         
62         if (tvb_reported_length_remaining(tvb, 0) < 4) {
63                 if (tree)
64                         proto_tree_add_text(tree, tvb, 0, -1, 
65                                             "Error processing Message");
66                 return;
67         }
68
69         if (dissect_try_cw_first_nibble(tvb, pinfo, tree)) 
70                 return;
71
72         sequence_number = tvb_get_ntohs(tvb, 2);
73         if (tree) {
74                 ti = proto_tree_add_boolean(tree, hf_pw_eth_cw, 
75                                             tvb, 0, 0, TRUE);
76                 PROTO_ITEM_SET_HIDDEN(ti);
77                 ti = proto_tree_add_item(tree, proto_pw_eth_cw, 
78                                          tvb, 0, 4, FALSE);
79                 pw_eth_tree = proto_item_add_subtree(ti, ett_pw_eth);
80                 if (pw_eth_tree == NULL)
81                         return;
82                 proto_tree_add_uint_format(pw_eth_tree, 
83                                            hf_pw_eth_cw_sequence_number,
84                                            tvb, 2, 2, sequence_number,
85                                            "Sequence Number: %d", 
86                                            sequence_number);
87         }
88         next_tvb = tvb_new_subset_remaining(tvb, 4);
89         {
90                 /*
91                  * When Ethernet frames being decoded, pinfo->ethertype is extracted 
92                  * from the top-level Ethernet frame. Dissection of Ethernet PW payload
93                  * overwrites this value as the same dissector is invoked again.
94                  * This may lead to undesired behavior (like disappearance of "Link"
95                  * tab from the "Decode as" menu).
96                  *
97                  * Let's save/restore ethertype. --ATA 
98                  *
99                  * XXX it looks that more pinfo members (or even the whole pinfo) 
100                  * XXX should be saved/restored in PW cases. Multilayer encapsulations, 
101                  * XXX like ethernet/mpls/ethernet-pw/ip/vlan, may lead to undesired 
102                  * XXX changes if pinfo->ipproto, ptype etc.
103                  */
104                 guint32 etype_save = pinfo->ethertype;
105                 call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree);
106                 pinfo->ethertype = etype_save;
107         }
108 }
109
110 static void
111 dissect_pw_eth_nocw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
112 {
113         tvbuff_t *next_tvb = NULL;
114         proto_item *ti = NULL;
115
116         if (tree) {
117                 ti = proto_tree_add_boolean(tree, hf_pw_eth, tvb, 0, 0, TRUE);
118                 PROTO_ITEM_SET_HIDDEN(ti);
119         }
120         next_tvb = tvb_new_subset_remaining(tvb, 0);
121         {
122                 guint32 etype_save = pinfo->ethertype;
123                 call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree);
124                 pinfo->ethertype = etype_save;
125         }
126 }
127
128 /* 
129  * FF: this function returns TRUE if the first 12 bytes in tvb looks like
130  *     two valid ethernet addresses.  FALSE otherwise. 
131  */
132 static gboolean 
133 looks_like_plain_eth(tvbuff_t *tvb _U_)
134 {
135         const gchar *manuf_name_da = NULL;
136         const gchar *manuf_name_sa = NULL;
137
138         if (tvb_reported_length_remaining(tvb, 0) < 14) {
139                 return FALSE;
140         }
141
142         manuf_name_da = get_manuf_name_if_known(tvb_get_ptr(tvb, 0, 6));
143         manuf_name_sa = get_manuf_name_if_known(tvb_get_ptr(tvb, 6, 6));
144
145         if (manuf_name_da && manuf_name_sa) {
146                 return TRUE;
147         }
148
149         return FALSE;
150 }
151
152 static void 
153 dissect_pw_eth_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
154 {
155         if (looks_like_plain_eth(tvb)) {
156                 call_dissector(pw_eth_handle_nocw, tvb, pinfo, tree);
157         } else {
158                 call_dissector(pw_eth_handle_cw, tvb, pinfo, tree);
159         }
160 }
161
162 void
163 proto_register_pw_eth(void)
164 {
165         static hf_register_info hf[] = {
166                 {
167                         &hf_pw_eth,
168                         {
169                                 "PW (ethernet)", 
170                                 "pweth", FT_BOOLEAN, 
171                                 BASE_NONE, NULL, 0x0, NULL, HFILL
172                         }
173                 },
174                 {
175                         &hf_pw_eth_cw,
176                         {
177                                 "PW Control Word (ethernet)", 
178                                 "pweth.cw", FT_BOOLEAN, 
179                                 BASE_NONE, NULL, 0x0, NULL, HFILL
180                         }
181                 },
182                 {
183                         &hf_pw_eth_cw_sequence_number,
184                         {
185                                 "PW sequence number (ethernet)", 
186                                 "pweth.cw.sequence_number", FT_UINT16, 
187                                 BASE_DEC, NULL, 0x0, NULL, HFILL
188                         }
189                 }
190         };
191
192         static gint *ett[] = {
193                 &ett_pw_eth
194         };
195
196         proto_pw_eth_cw = 
197           proto_register_protocol("PW Ethernet Control Word",
198                                   "Ethernet PW (with CW)",
199                                   "pwethcw");
200         proto_pw_eth_nocw = 
201           proto_register_protocol("Ethernet PW (no CW)", /* not displayed */
202                                   "Ethernet PW (no CW)",
203                                   "pwethnocw");
204         proto_pw_eth_heuristic = 
205           proto_register_protocol("Ethernet PW (CW heuristic)", /* not disp. */
206                                   "Ethernet PW (CW heuristic)", 
207                                   "pwethheuristic");
208         proto_register_field_array(proto_pw_eth_cw, hf, array_length(hf));
209         proto_register_subtree_array(ett, array_length(ett));   
210         register_dissector("pw_eth_cw", dissect_pw_eth_cw, proto_pw_eth_cw);
211         register_dissector("pw_eth_nocw", dissect_pw_eth_nocw, 
212                            proto_pw_eth_nocw);
213         register_dissector("pw_eth_heuristic", dissect_pw_eth_heuristic, 
214                            proto_pw_eth_heuristic);
215 }
216
217 void
218 proto_reg_handoff_pw_eth(void)
219 {
220         dissector_handle_t pw_eth_handle_heuristic;
221
222         eth_withoutfcs_handle = find_dissector("eth_withoutfcs");
223
224         pw_eth_handle_cw = find_dissector("pw_eth_cw");
225         dissector_add("mpls.label", LABEL_INVALID, pw_eth_handle_cw);
226
227         pw_eth_handle_nocw = find_dissector("pw_eth_nocw");
228         dissector_add("mpls.label", LABEL_INVALID, pw_eth_handle_nocw);
229
230         pw_eth_handle_heuristic = find_dissector("pw_eth_heuristic");
231         dissector_add("mpls.label", LABEL_INVALID, pw_eth_handle_heuristic);
232 }