Don't guard col_set_str (COL_PROTOCOL) with col_check
[obnox/wireshark/wip.git] / epan / dissectors / packet-ieee8021ah.c
1 /* packet-ieee8021ah.c
2  * Routines for 802.1ah ethernet header disassembly
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <glib.h>
30 #include <epan/packet.h>
31 #include <epan/addr_resolv.h>
32 #include "packet-ieee8023.h"
33 #include "packet-ieee8021ah.h"
34 #include "packet-ipx.h"
35 #include "packet-llc.h"
36 #include "packet-vlan.h"
37 #include <epan/etypes.h>
38 #include <epan/prefs.h>
39
40 void proto_reg_handoff_ieee8021ah(void);
41 void dissect_ieee8021ah_common(tvbuff_t *tvb, packet_info *pinfo, 
42                                proto_tree *tree, proto_tree *parent, int tree_index);
43
44 /* GLOBALS ************************************************************/
45
46 /* ethertype for 802.1ah tag - encapsulating an Ethernet packet */
47 static unsigned int ieee8021ah_ethertype = ETHERTYPE_IEEE_802_1AH;
48
49 static int proto_ieee8021ah = -1;
50 static int proto_ieee8021ad = -1;
51
52 /* dot1ad B-tag fields */
53 static int hf_ieee8021ad_priority = -1;
54 static int hf_ieee8021ad_cfi = -1;
55 static int hf_ieee8021ad_id = -1;
56 static int hf_ieee8021ad_svid = -1;
57 static int hf_ieee8021ad_cvid = -1;
58
59 /* dot1ah C-tag fields */
60 static int hf_ieee8021ah_priority = -1;
61 static int hf_ieee8021ah_drop = -1;   /* drop eligibility */
62 static int hf_ieee8021ah_nca = -1;    /* no customer addresses (c_daddr & c_saddr are 0) */
63 static int hf_ieee8021ah_res1 = -1;   /* 2 bits reserved; ignored on receive */
64 static int hf_ieee8021ah_res2 = -1;   /* 2 bits reserved; delete frame if non-zero */
65 static int hf_ieee8021ah_isid = -1;     /* I-SID */
66 static int hf_ieee8021ah_c_daddr = -1;  /* encapsulated customer dest addr */
67 static int hf_ieee8021ah_c_saddr = -1;  /* encapsulated customer src addr */
68
69 static int hf_ieee8021ah_etype = -1;
70 static int hf_ieee8021ah_len = -1;
71 static int hf_ieee8021ah_trailer = -1;
72
73 static gint ett_ieee8021ah = -1;
74 static gint ett_ieee8021ad = -1;
75
76 /* FUNCTIONS ************************************************************/
77
78
79 void
80 capture_ieee8021ah(const guchar *pd, int offset, int len, packet_counts *ld)
81 {
82     guint16 encap_proto;
83
84     if (!BYTES_ARE_IN_FRAME(offset, len, IEEE8021AH_LEN + 1)) {
85         ld->other++;
86         return;
87     }
88     encap_proto = pntohs( &pd[offset + IEEE8021AH_LEN - 2] );
89     if (encap_proto <= IEEE_802_3_MAX_LEN) {
90         if ( pd[offset + IEEE8021AH_LEN] == 0xff 
91              && pd[offset + IEEE8021AH_LEN + 1] == 0xff ) {
92             capture_ipx(ld);
93         } 
94         else {
95             capture_llc(pd, offset + IEEE8021AH_LEN,len,ld);
96         }
97     } 
98     else {
99         capture_ethertype(encap_proto, pd, offset + IEEE8021AH_LEN, len, ld);
100     }
101 }
102
103 /* Dissector *************************************************************/
104 static 
105 void
106 dissect_ieee8021ad(tvbuff_t *tvb, packet_info *pinfo, 
107                    proto_tree *tree)
108 {
109     proto_tree *ptree = NULL;
110     proto_tree *tagtree = NULL;
111     guint32 tci, ctci;
112     guint16 encap_proto;
113     proto_tree *volatile ieee8021ad_tree;
114     proto_tree *volatile ieee8021ad_tag_tree;
115     int proto_tree_index;
116     tvbuff_t *volatile next_tvb = NULL;
117
118     /* set tree index */    
119     proto_tree_index = proto_ieee8021ad;
120
121     /* add info to column display */
122     col_set_str(pinfo->cinfo, COL_PROTOCOL, "802.1ad");
123     if (check_col(pinfo->cinfo, COL_INFO))
124         col_clear(pinfo->cinfo, COL_INFO);
125
126     tci = tvb_get_ntohs( tvb, 0 );
127
128     if (check_col(pinfo->cinfo, COL_INFO)) {
129         col_add_fstr(pinfo->cinfo, COL_INFO, 
130                      "PRI: %d  DROP: %d ID: %d",
131                      (tci >> 13), ((tci >> 12) & 1), (tci & 0xFFF));
132     }
133
134     /* create the protocol tree */
135     ieee8021ad_tree = NULL;
136
137     if (tree) {
138         ptree = proto_tree_add_item(tree, proto_tree_index, tvb, 0, IEEE8021AD_LEN, FALSE);
139         ieee8021ad_tree = proto_item_add_subtree(ptree, ett_ieee8021ad);
140     }
141
142     encap_proto = tvb_get_ntohs(tvb, IEEE8021AD_LEN - 2);
143
144     /* If it's a 1ah frame, create subtree for B-Tag, rename overall
145        tree to 802.1ah, pass to 1ah dissector */
146     if (encap_proto == ETHERTYPE_IEEE_802_1AH) {
147         if (tree) {
148             tagtree = proto_tree_add_item(ptree, proto_tree_index, tvb, 0, 2, FALSE);
149             ieee8021ad_tag_tree = proto_item_add_subtree(tagtree, ett_ieee8021ad);
150
151             /* add fields */
152             proto_tree_add_uint(ieee8021ad_tag_tree, hf_ieee8021ad_priority, tvb, 
153                                 0, 1, tci);
154             proto_tree_add_uint(ieee8021ad_tag_tree, hf_ieee8021ad_cfi, tvb, 0, 1, tci);
155             proto_tree_add_uint(ieee8021ad_tag_tree, hf_ieee8021ad_id, tvb, 0, 2, tci);
156
157             /* set label of B-tag subtree */
158             proto_item_set_text(ieee8021ad_tag_tree, "B-Tag, B-VID: %d", tci & 0x0FFF);
159         }
160
161         next_tvb = tvb_new_subset(tvb, IEEE8021AD_LEN, -1, -1);
162
163         if (ptree) {
164             /* add bvid to label */
165             proto_item_set_text(ptree, "IEEE 802.1ah, B-VID: %d", tci & 0x0FFF);
166
167             dissect_ieee8021ah_common(next_tvb, pinfo, ptree, tree, proto_tree_index);
168         }
169         else {
170             dissect_ieee8021ah_common(next_tvb, pinfo, tree, NULL, proto_tree_index);
171         }
172
173         return;
174     } else if (encap_proto == ETHERTYPE_IEEE_802_1AD) {
175         /* two VLAN tags (i.e. Q-in-Q) */
176         ctci = tvb_get_ntohs(tvb, IEEE8021AD_LEN);
177
178         if (tree) {
179             /* add fields */
180             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_priority, tvb, 
181                                 0, 1, tci);
182             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_cfi, tvb, 0, 1, tci);
183             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_svid, tvb, 0, 2, tci);
184             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_priority, tvb, 
185                                 IEEE8021AD_LEN, 1, ctci);
186             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_cfi, tvb,
187                                 IEEE8021AD_LEN, 1, ctci);
188             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_cvid, tvb, IEEE8021AD_LEN, 
189                                 2, ctci);
190         }
191
192         proto_item_set_text(ptree, "IEEE 802.1ad, S-VID: %d, C-VID: %d", tci & 0x0FFF,
193                             ctci & 0x0FFF);
194
195         /* 802.1ad tags are always followed by an ethertype; call next
196            dissector based on ethertype */
197         encap_proto = tvb_get_ntohs(tvb, IEEE8021AD_LEN * 2 - 2);
198         ethertype(encap_proto, tvb, IEEE8021AD_LEN * 2, pinfo, tree, ieee8021ad_tree,
199                   hf_ieee8021ah_etype, hf_ieee8021ah_trailer, 0);
200     } else {
201         /* Something else (shouldn't really happen, but we'll support it anyways) */
202         if (tree) {
203             /* add fields */
204             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_priority, tvb, 
205                                 0, 1, tci);
206             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_cfi, tvb, 0, 1, tci);
207             proto_tree_add_uint(ieee8021ad_tree, hf_ieee8021ad_id, tvb, 0, 2, tci);
208         }
209
210         /* label should be 802.1ad not .1ah */
211         proto_item_set_text(ptree, "IEEE 802.1ad, ID: %d", tci & 0x0FFF);
212
213         /* 802.1ad tags are always followed by an ethertype; call next
214            dissector based on ethertype */
215         ethertype(encap_proto, tvb, IEEE8021AD_LEN, pinfo, tree, ieee8021ad_tree,
216                   hf_ieee8021ah_etype, hf_ieee8021ah_trailer, 0);
217     }
218 }
219
220 void
221 dissect_ieee8021ah_common(tvbuff_t *tvb, packet_info *pinfo, 
222                           proto_tree *tree, proto_tree *parent, int tree_index) {
223     guint32 tci;
224     guint16 encap_proto;
225     proto_tree *ptree;
226     proto_tree *volatile ieee8021ah_tag_tree;
227
228     /* for parsing out ethernet addrs */
229     proto_item *addr_item;
230     const guint8 *src_addr, *dst_addr;
231
232     tci = tvb_get_ntohl( tvb, 0 );
233
234     if (check_col(pinfo->cinfo, COL_INFO)) {
235         col_add_fstr(pinfo->cinfo, COL_INFO, 
236                      "PRI: %d  Drop: %d  NCA: %d  Res1: %d  Res2: %d  I-SID: %d",
237                      (tci >> 29), ((tci >> 28) & 1), ((tci >> 27) & 1),
238                      ((tci >> 26) & 1), ((tci >> 24) & 3), tci & IEEE8021AH_ISIDMASK);
239     }
240
241     /* create the protocol tree */
242     ptree = NULL;
243     ieee8021ah_tag_tree = NULL;
244
245     if (tree) {
246         /* 802.1ah I-Tag */
247         ptree = proto_tree_add_item(tree, tree_index, tvb, 0, 4, FALSE);
248         ieee8021ah_tag_tree = proto_item_add_subtree(ptree, ett_ieee8021ah);
249
250         /* add fields */
251         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_priority, tvb, 
252                             0, 1, tci);
253         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_drop, tvb, 0, 1, tci);
254         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_nca, tvb, 0, 1, tci);
255         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_res1, tvb, 0, 1, tci);
256         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_res2, tvb, 0, 1, tci);
257         proto_tree_add_uint(ieee8021ah_tag_tree, hf_ieee8021ah_isid, tvb, 1, 3, tci);
258
259         proto_item_set_text(ieee8021ah_tag_tree, "I-Tag, I-SID: %d",
260                             tci & IEEE8021AH_ISIDMASK);
261
262         /* ensure size of tag */
263         tvb_ensure_bytes_exist(tvb, 4, 12);
264
265         /* parse out IP addrs */
266         dst_addr = tvb_get_ptr(tvb, 4, 6); /* safe to use this function? */
267         src_addr = tvb_get_ptr(tvb, 10, 6);
268
269         addr_item = proto_tree_add_ether(tree, hf_ieee8021ah_c_daddr, 
270                                          tvb, 4, 6, dst_addr);
271
272         addr_item = proto_tree_add_ether(tree, hf_ieee8021ah_c_saddr, 
273                                          tvb, 10, 6, src_addr);
274
275         /* add text to 802.1ad label */
276         if (parent) {
277             proto_item_append_text(tree, ", I-SID: %d, C-Src: %s (%s), C-Dst: %s (%s)", 
278                                    tci & IEEE8021AH_ISIDMASK, get_ether_name(src_addr), 
279                                    ether_to_str(src_addr), get_ether_name(dst_addr),
280                                    ether_to_str(dst_addr));
281         }
282     }
283
284     encap_proto = tvb_get_ntohs(tvb, IEEE8021AH_LEN - 2);
285
286     /* 802.1ah I-tags are always followed by an ethertype; call next
287        dissector based on ethertype */
288
289     /* If this was preceded by a 802.1ad tag, must pass original tree
290        to next dissector, not 802.1ad tree */
291     if (parent) {
292         ethertype(encap_proto, tvb, IEEE8021AH_LEN, pinfo, parent, tree,
293                   hf_ieee8021ah_etype, hf_ieee8021ah_trailer, 0);
294     }
295     else {
296         ethertype(encap_proto, tvb, IEEE8021AH_LEN, pinfo, tree, tree,
297                   hf_ieee8021ah_etype, hf_ieee8021ah_trailer, 0);
298     }
299 }
300
301 static
302 void
303 dissect_ieee8021ah(tvbuff_t *tvb, packet_info *pinfo, 
304                    proto_tree *tree)
305 {
306     proto_tree *ptree;
307     guint32 tci;
308     proto_tree *volatile ieee8021ah_tree;
309     int proto_tree_index;
310
311     /* set tree index */    
312     proto_tree_index = proto_ieee8021ah;
313
314     /* add info to column display */
315     col_set_str(pinfo->cinfo, COL_PROTOCOL, "802.1ah");
316     if (check_col(pinfo->cinfo, COL_INFO))
317         col_clear(pinfo->cinfo, COL_INFO);
318
319     tci = tvb_get_ntohl( tvb, 0 );
320
321     if (check_col(pinfo->cinfo, COL_INFO)) {
322         col_add_fstr(pinfo->cinfo, COL_INFO, 
323                      "PRI: %d  Drop: %d  NCA: %d  Res1: %d  Res2: %d  I-SID: %d",
324                      (tci >> 29), ((tci >> 28) & 1), ((tci >> 27) & 1),
325                      ((tci >> 26) & 1), ((tci >> 24) & 3), (tci & 0x00FFFFFF));
326     }
327
328     /* create the protocol tree */
329     ieee8021ah_tree = NULL;
330
331     if (tree) {
332         ptree = proto_tree_add_item(tree, proto_tree_index, tvb, 0, IEEE8021AH_LEN, FALSE);
333         ieee8021ah_tree = proto_item_add_subtree(ptree, ett_ieee8021ah);
334
335         dissect_ieee8021ah_common(tvb, pinfo, ptree, tree, proto_tree_index);
336     }
337 }
338
339 /* Protocol Registration **************************************************/
340
341 void
342 proto_register_ieee8021ah(void)
343 {
344     static hf_register_info hf[] = {
345         { &hf_ieee8021ah_priority, {
346             "Priority", "ieee8021ah.priority", FT_UINT32, BASE_DEC,
347             0, 0xE0000000, NULL, HFILL }},
348         { &hf_ieee8021ah_drop, {
349             "DROP", "ieee8021ah.drop", FT_UINT32, BASE_DEC,
350             0, 0x10000000, "Drop", HFILL }},
351         { &hf_ieee8021ah_nca, {
352             "NCA", "ieee8021ah.nca", FT_UINT32, BASE_DEC,
353             0, 0x08000000, "No Customer Addresses", HFILL }},
354         { &hf_ieee8021ah_res1, {
355             "RES1", "ieee8021ah.res1", FT_UINT32, BASE_DEC,
356             0, 0x04000000, "Reserved1", HFILL }},
357         { &hf_ieee8021ah_res2, {
358             "RES2", "ieee8021ah.res2", FT_UINT32, BASE_DEC,
359             0, 0x03000000, "Reserved2", HFILL }},
360         { &hf_ieee8021ah_isid, {
361             "I-SID", "ieee8021ah.isid", FT_UINT32, BASE_DEC,
362             0, 0x00FFFFFF, NULL, HFILL }},
363         { &hf_ieee8021ah_c_daddr, {
364             "C-Destination", "ieee8021ah.cdst", FT_ETHER, BASE_NONE,
365             NULL, 0x0, "Customer Destination Address", HFILL }},
366         { &hf_ieee8021ah_c_saddr, {
367             "C-Source", "ieee8021ah.csrc", FT_ETHER, BASE_NONE,
368             NULL, 0x0, "Customer Source Address", HFILL }},
369         { &hf_ieee8021ah_etype, {
370                 "Type", "ieee8021ah.etype", FT_UINT16, BASE_HEX,
371                 VALS(etype_vals), 0x0, NULL, HFILL }},
372         { &hf_ieee8021ah_len, {
373                 "Length", "ieee8021ah.len", FT_UINT16, BASE_DEC,
374                 NULL, 0x0, NULL, HFILL }},
375         { &hf_ieee8021ah_trailer, {
376                 "Trailer", "ieee8021ah.trailer", FT_BYTES, BASE_NONE,
377                 NULL, 0x0, "802.1ah Trailer", HFILL }}
378     };
379
380     static hf_register_info hf_1ad[] = {
381         { &hf_ieee8021ad_priority, {
382                 "Priority", "ieee8021ad.priority", FT_UINT16, BASE_DEC,
383                 0, 0xE000, NULL, HFILL }},
384         { &hf_ieee8021ad_cfi, {
385                 "DEI", "ieee8021ad.dei", FT_UINT16, BASE_DEC,
386                 0, 0x1000, "Drop Eligibility", HFILL }},
387         { &hf_ieee8021ad_id, {
388                 "ID", "ieee8021ad.id", FT_UINT16, BASE_DEC,
389                 0, 0x0FFF, "Vlan ID", HFILL }},
390         { &hf_ieee8021ad_svid, {
391                 "ID", "ieee8021ad.svid", FT_UINT16, BASE_DEC,
392                 0, 0x0FFF, "S-Vlan ID", HFILL }},
393         { &hf_ieee8021ad_cvid, {
394                 "ID", "ieee8021ad.cvid", FT_UINT16, BASE_DEC,
395                 0, 0x0FFF, "C-Vlan ID", HFILL }},
396     };
397
398     static gint *ett[] = {
399         &ett_ieee8021ah,
400         &ett_ieee8021ad
401     };
402
403
404     module_t *ieee8021ah_module;
405
406     /* registration */
407     /* dot1ah */
408     proto_ieee8021ah = proto_register_protocol("IEEE 802.1ah", "IEEE 802.1AH",
409                                                "ieee8021ah");
410     proto_register_field_array(proto_ieee8021ah, hf, array_length(hf));
411
412     proto_ieee8021ad = proto_register_protocol("IEEE 802.1ad", "IEEE 802.1AD",
413                                                "ieee8021ad");
414     proto_register_field_array(proto_ieee8021ad, hf_1ad, array_length(hf_1ad));
415
416     /* register subtree array for both */
417     proto_register_subtree_array(ett, array_length(ett));
418
419     /* add a user preference to set the 802.1ah ethertype */
420     ieee8021ah_module = prefs_register_protocol(proto_ieee8021ah, 
421                                                 proto_reg_handoff_ieee8021ah);
422     prefs_register_uint_preference(ieee8021ah_module, "8021ah_ethertype",
423                                    "802.1ah Ethertype (in hex)",
424                                    "(Hexadecimal) Ethertype used to indicate IEEE 802.1ah tag.",
425                                    16, &ieee8021ah_ethertype);
426 }
427
428 void
429 proto_reg_handoff_ieee8021ah(void)
430 {
431     static gboolean prefs_initialized = FALSE;
432     static dissector_handle_t ieee8021ah_handle;
433     static unsigned int old_ieee8021ah_ethertype;
434
435     if (!prefs_initialized){
436         dissector_handle_t ieee8021ad_handle;
437         ieee8021ah_handle = create_dissector_handle(dissect_ieee8021ah, 
438                                                     proto_ieee8021ah);  
439         ieee8021ad_handle = create_dissector_handle(dissect_ieee8021ad,
440                                                     proto_ieee8021ad);
441         dissector_add("ethertype", ETHERTYPE_IEEE_802_1AD, ieee8021ad_handle);
442         prefs_initialized = TRUE;
443     }
444     else {
445         dissector_delete("ethertype", old_ieee8021ah_ethertype, ieee8021ah_handle);
446     }
447
448     old_ieee8021ah_ethertype = ieee8021ah_ethertype;
449     dissector_add("ethertype", ieee8021ah_ethertype, ieee8021ah_handle);
450
451 }