1 /* packet-ecp-oui.c
2  * ECP/VDP dissector for wireshark (according to IEEE 802.1Qbg draft 0)
3  * By Jens Osterkamp <jens at linux.vnet.ibm.com>
4  *    Mijo Safradin <mijo at linux.vnet.ibm.com>
5  * Copyright 2011,2012  IBM Corp.
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  */
13 
14 #include "config.h"
15 
16 #include <epan/packet.h>
17 #include <epan/addr_resolv.h>
18 #include <epan/oui.h>
19 #include <epan/addr_resolv.h>
20 
21 #include <wsutil/str_util.h>
22 
23 #include "packet-ieee802a.h"
24 
25 void proto_register_ecp_oui(void);
26 void proto_reg_handoff_ecp(void);
27 
28 #define ECP_SUBTYPE		0x00
29 
30 #define END_OF_VDPDU_TLV_TYPE	0x00	/* Mandatory */
31 #define VDP_TLV_TYPE		0x02
32 #define ORG_SPECIFIC_TLV_TYPE	0x7F
33 
34 /* IEEE 802.1Qbg VDP filter info formats */
35 #define VDP_FIF_VID		0x01
36 #define VDP_FIF_MACVID		0x02
37 #define VDP_FIF_GROUPVID	0x03
38 #define VDP_FIF_GROUPVMACVID	0x04
39 
40 /* Masks */
41 #define TLV_TYPE_MASK		0xFE00
42 #define TLV_TYPE(value)		(((value) & TLV_TYPE_MASK) >> 9)
43 #define TLV_INFO_LEN_MASK	0x01FF
44 #define TLV_INFO_LEN(value)	((value) & TLV_INFO_LEN_MASK)
45 
46 static gint proto_ecp = -1;
47 static gint hf_ecp_pid = -1;
48 static gint hf_ecp_tlv_type = -1;
49 static gint hf_ecp_tlv_len = -1;
50 static gint hf_ecp_subtype = -1;
51 static gint hf_ecp_mode = -1;
52 static gint hf_ecp_sequence = -1;
53 /* static gint hf_ecp_vdp_oui = -1; */
54 static gint hf_ecp_vdp_mode = -1;
55 static gint hf_ecp_vdp_response = -1;
56 static gint hf_ecp_vdp_mgrid = -1;
57 static gint hf_ecp_vdp_vsitypeid = -1;
58 static gint hf_ecp_vdp_vsitypeidversion = -1;
59 static gint hf_ecp_vdp_instanceid = -1;
60 static gint hf_ecp_vdp_format = -1;
61 static gint hf_ecp_vdp_mac = -1;
62 static gint hf_ecp_vdp_vlan = -1;
63 
64 static gint ett_ecp = -1;
65 static gint ett_end_of_vdpdu = -1;
66 static gint ett_802_1qbg_capabilities_flags = -1;
67 
68 static dissector_handle_t ecp_handle;
69 
70 static const value_string ecp_pid_vals[] = {
71 	{ 0x0000,	"ECP draft 0" },
72 	{ 0,		NULL }
73 };
74 
75 /* IEEE 802.1Qbg  ECP subtypes */
76 static const value_string ecp_subtypes[] = {
77 	{ 0x00,	"ECP default subtype" },
78 	{ 0, NULL }
79 };
80 
81 /* IEEE 802.1Qbg ECP modes */
82 static const value_string ecp_modes[] = {
83 	{ 0x00,	"REQUEST" },
84 	{ 0x01,	"ACK" },
85 	{ 0, NULL }
86 };
87 
88 /* IEEE 802.1Qbg VDP modes */
89 static const value_string ecp_vdp_modes[] = {
90 	{ 0x00,	"Pre-Associate" },
91 	{ 0x01,	"Pre-Associate with resource reservation" },
92 	{ 0x02,	"Associate" },
93 	{ 0x03,	"De-Associate" },
94 	{ 0, NULL }
95 };
96 
97 /* IEEE 802.1Qbg VDP responses */
98 static const value_string ecp_vdp_responses[] = {
99 	{ 0x00, "success" },
100 	{ 0x01, "invalid format" },
101 	{ 0x02, "insufficient resources" },
102 	{ 0x03, "unused VTID" },
103 	{ 0x04, "VTID violation" },
104 	{ 0x05, "VTID version violation" },
105 	{ 0x06, "out of sync" },
106 	{ 0, NULL }
107 };
108 
109 /* IEEE 802.1Qbg VDP filter info formats */
110 static const value_string ecp_vdp_formats[] = {
111 	{ VDP_FIF_VID, "VID values" },
112 	{ VDP_FIF_MACVID, "MAC/VID pairs" },
113 	{ VDP_FIF_GROUPVID, "GROUPID/VID pairs" },
114 	{ VDP_FIF_GROUPVMACVID, "GROUPID/MAC/VID triples" },
115 	{ 0, NULL }
116 };
117 
118 /* IEEE 802.1Qbg Subtypes */
119 static const value_string ieee_802_1qbg_subtypes[] = {
120 	{ 0x00,	"EVB" },
121 	{ 0x01,	"CDCP" },
122 	{ 0x02,	"VDP" },
123 	{ 0, NULL }
124 };
125 
126 /* Dissect Unknown TLV */
127 static gint32
dissect_ecp_unknown_tlv(tvbuff_t * tvb,packet_info * pinfo _U_,proto_tree * tree,guint32 offset)128 dissect_ecp_unknown_tlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset)
129 {
130 	guint16 tempLen;
131 	guint16 tempShort;
132 
133 	proto_tree *ecp_unknown_tlv_tree;
134 
135 	/* Get tlv type and length */
136 	tempShort = tvb_get_ntohs(tvb, offset);
137 
138 	/* Get tlv length */
139 	tempLen = TLV_INFO_LEN(tempShort);
140 
141 	ecp_unknown_tlv_tree = proto_tree_add_subtree(tree, tvb, offset, (tempLen + 2), ett_ecp, NULL, "Unknown TLV");
142 
143 	proto_tree_add_item(ecp_unknown_tlv_tree, hf_ecp_subtype, tvb, offset, 2, ENC_BIG_ENDIAN);
144 
145 	return -1;
146 }
147 
148 /* Dissect mac/vid pairs in VDP TLVs */
149 static gint32
dissect_vdp_fi_macvid(tvbuff_t * tvb,packet_info * pinfo _U_,proto_tree * tree,guint32 offset)150 dissect_vdp_fi_macvid(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset)
151 {
152 	gint i;
153 	guint16 entries;
154 	guint32 tempOffset = offset;
155 
156 	proto_tree *ecp_vdp_tlv_fi_subtree;
157 
158 	entries = tvb_get_ntohs(tvb, offset);
159 
160 	ecp_vdp_tlv_fi_subtree = proto_tree_add_subtree_format(tree, tvb, tempOffset, 2, ett_ecp, NULL,
161 			"%i MAC/VID pair%s", entries, plurality(entries, "", "s"));
162 
163 	tempOffset += 2;
164 
165 	for (i=0; i < entries; i++) {
166 		proto_tree_add_item(ecp_vdp_tlv_fi_subtree, hf_ecp_vdp_mac, tvb, tempOffset, 6, ENC_NA);
167 
168 		tempOffset += 6;
169 
170 		proto_tree_add_item(ecp_vdp_tlv_fi_subtree, hf_ecp_vdp_vlan, tvb, tempOffset, 2, ENC_BIG_ENDIAN);
171 
172 		tempOffset += 2;
173 	}
174 
175 	return tempOffset-offset;
176 }
177 
178 /* Dissect Organizationally Defined TLVs */
179 static gint32
dissect_vdp_org_specific_tlv(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset)180 dissect_vdp_org_specific_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
181 {
182 	guint16 tempLen;
183 	guint16 len;
184 	guint16 tempShort;
185 	guint32 tempOffset = offset;
186 	guint32 oui;
187 	const char *ouiStr;
188 	guint8 subType, format;
189 	const char *subTypeStr;
190 
191 	proto_tree	*ecp_vdp_tlv_subtree;
192 
193 	tempLen = 0;
194 	tempShort = tvb_get_ntohs(tvb, offset);
195 	len = TLV_INFO_LEN(tempShort);
196 
197 	tempOffset += 2;
198 
199 	oui = tvb_get_ntoh24(tvb, (tempOffset));
200 	/* Look in manuf database for OUI */
201 	ouiStr = uint_get_manuf_name_if_known(oui);
202 	if(ouiStr==NULL) ouiStr="Unknown";
203 
204 	tempOffset += 3;
205 
206 	subType = tvb_get_guint8(tvb, tempOffset);
207 	tempOffset++;
208 
209 	switch(oui) {
210 	case OUI_IEEE_802_1QBG:
211 		subTypeStr = val_to_str(subType, ieee_802_1qbg_subtypes, "Unknown subtype 0x%x");
212 		break;
213 	default:
214 		subTypeStr = "Unknown";
215 		break;
216 	}
217 
218 	ecp_vdp_tlv_subtree = proto_tree_add_subtree_format(tree, tvb, offset, (len + 2), ett_ecp, NULL,
219 			"%s - %s", ouiStr, subTypeStr);
220 
221 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_mode, tvb, tempOffset, 1, ENC_BIG_ENDIAN);
222 	tempOffset++;
223 
224 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_response, tvb, tempOffset, 1, ENC_BIG_ENDIAN);
225 	tempOffset++;
226 
227 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_mgrid, tvb, tempOffset, 1, ENC_BIG_ENDIAN);
228 	tempOffset++;
229 
230 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_vsitypeid, tvb, tempOffset, 3, ENC_BIG_ENDIAN);
231 	tempOffset += 3;
232 
233 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_vsitypeidversion, tvb, tempOffset, 1, ENC_BIG_ENDIAN);
234 	tempOffset += 1;
235 
236 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_instanceid, tvb, tempOffset, 16, ENC_NA);
237 	tempOffset += 16;
238 
239 	format = tvb_get_guint8(tvb, tempOffset);
240 	proto_tree_add_item(ecp_vdp_tlv_subtree, hf_ecp_vdp_format, tvb, tempOffset, 1, ENC_BIG_ENDIAN);
241 	tempOffset++;
242 
243 	switch (format) {
244 	case VDP_FIF_VID:
245 		/* place holder for future enablement */
246 		/* For compatibility of different implementations proceed to next entry */
247 	case VDP_FIF_MACVID:
248 		tempLen = dissect_vdp_fi_macvid(tvb, pinfo, ecp_vdp_tlv_subtree, tempOffset);
249 		break;
250 	case VDP_FIF_GROUPVID:
251 		/* place holder for future enablement */
252 		break;
253 	case VDP_FIF_GROUPVMACVID:
254 		/* place holder for future enablement */
255 		break;
256 	default:
257 		break;
258 	}
259 
260 	tempOffset += tempLen;
261 
262 	return tempOffset-offset;
263 }
264 
265 /* Dissect End of VDP TLV (Mandatory) */
266 static gint32
dissect_vdp_end_of_vdpdu_tlv(tvbuff_t * tvb,packet_info * pinfo _U_,proto_tree * tree,guint32 offset)267 dissect_vdp_end_of_vdpdu_tlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset)
268 {
269 	guint16 tempLen;
270 	guint16 tempShort;
271 
272 	proto_tree	*end_of_vdpdu_tree;
273 
274 	/* Get tlv type and length */
275 	tempShort = tvb_get_ntohs(tvb, offset);
276 
277 	/* Get tlv length */
278 	tempLen = TLV_INFO_LEN(tempShort);
279 
280 	if (tree)
281 	{
282 		/* Set port tree */
283 		end_of_vdpdu_tree = proto_tree_add_subtree(tree, tvb, offset, (tempLen + 2),
284 										ett_end_of_vdpdu, NULL, "End of VDPDU");
285 
286 		proto_tree_add_item(end_of_vdpdu_tree, hf_ecp_tlv_type, tvb, offset, 2, ENC_BIG_ENDIAN);
287 		proto_tree_add_item(end_of_vdpdu_tree, hf_ecp_tlv_len, tvb, offset, 2, ENC_BIG_ENDIAN);
288 	}
289 
290 	return -1;	/* Force the VDP dissector to terminate */
291 }
292 
293 static int
dissect_ecp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)294 dissect_ecp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
295 {
296 	proto_tree *ecp_tree;
297 	proto_item *ti;
298 	gint32 tempLen = 0;
299 	guint32 offset = 0;
300 	guint16 tempShort;
301 	guint8 tempType;
302 	gboolean end = FALSE;
303 
304 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "ECP");
305 
306 	ti = proto_tree_add_item(tree, proto_ecp, tvb, 0, -1, ENC_NA);
307 	ecp_tree = proto_item_add_subtree(ti, ett_ecp);
308 
309 	proto_tree_add_item(ecp_tree, hf_ecp_subtype, tvb, offset, 1, ENC_BIG_ENDIAN);
310 	proto_tree_add_item(ecp_tree, hf_ecp_mode, tvb, offset+1, 1, ENC_BIG_ENDIAN);
311 	proto_tree_add_item(ecp_tree, hf_ecp_sequence, tvb, offset+2, 2, ENC_BIG_ENDIAN);
312 
313 	offset += 4;
314 
315 	while (!end) {
316 		if (!tvb_bytes_exist(tvb, offset, 1))
317 			break;
318 
319 		tempShort = tvb_get_ntohs(tvb, offset);
320 		tempType = TLV_TYPE(tempShort);
321 
322 		switch (tempType) {
323 		case ORG_SPECIFIC_TLV_TYPE:
324 			tempLen = dissect_vdp_org_specific_tlv(tvb, pinfo, ecp_tree, offset);
325 			break;
326 		case END_OF_VDPDU_TLV_TYPE:
327 			tempLen = dissect_vdp_end_of_vdpdu_tlv(tvb, pinfo, ecp_tree, offset);
328 			break;
329 		default:
330 			tempLen = dissect_ecp_unknown_tlv(tvb, pinfo, ecp_tree, offset);
331 			break;
332 		}
333 
334 		offset += tempLen;
335 
336 		if (tempLen < 0)
337 			end = TRUE;
338 	}
339 	return tvb_captured_length(tvb);
340 }
341 
proto_register_ecp_oui(void)342 void proto_register_ecp_oui(void)
343 {
344 	static hf_register_info hf_reg = {
345 		&hf_ecp_pid,
346 		{ "PID", "ieee802a.ecp_pid", FT_UINT16, BASE_HEX,
347 			VALS(ecp_pid_vals), 0x0, NULL, HFILL },
348 	};
349 
350 	static hf_register_info hf[] = {
351 		{ &hf_ecp_tlv_type,
352 			{ "TLV Type", "ecp.tlv.type", FT_UINT16, BASE_DEC,
353 			NULL, TLV_TYPE_MASK, NULL, HFILL }
354 		},
355 		{ &hf_ecp_tlv_len,
356 			{ "TLV Length", "ecp.tlv.len", FT_UINT16, BASE_DEC,
357 			NULL, TLV_INFO_LEN_MASK, NULL, HFILL }
358 		},
359 
360 		{ &hf_ecp_subtype,
361 			{ "subtype", "ecp.subtype", FT_UINT8, BASE_HEX,
362 				VALS(ecp_subtypes), 0x0, NULL, HFILL },
363 		},
364 		{ &hf_ecp_mode,
365 			{ "mode", "ecp.mode", FT_UINT8, BASE_HEX,
366 				VALS(ecp_modes), 0x0, NULL, HFILL },
367 		},
368 		{ &hf_ecp_sequence,
369 			{ "sequence number", "ecp.seq", FT_UINT16, BASE_HEX,
370 				NULL, 0x0, NULL, HFILL },
371 		},
372 #if 0
373 		{ &hf_ecp_vdp_oui,
374 			{ "Organization Unique Code",	"ecp.vdp.oui", FT_UINT24, BASE_OUI,
375 			NULL, 0x0, NULL, HFILL }
376 		},
377 #endif
378 		{ &hf_ecp_vdp_mode,
379 			{ "mode", "ecp.vdp.mode", FT_UINT8, BASE_HEX,
380 				VALS(ecp_vdp_modes), 0x0, NULL, HFILL },
381 		},
382 		{ &hf_ecp_vdp_response,
383 			{ "response", "ecp.vdp.response", FT_UINT8, BASE_HEX,
384 				VALS(ecp_vdp_responses), 0x0, NULL, HFILL },
385 		},
386 		{ &hf_ecp_vdp_mgrid,
387 			{ "Manager ID", "ecp.vdp.mgrid", FT_UINT8, BASE_HEX,
388 				NULL, 0x0, NULL, HFILL },
389 		},
390 		{ &hf_ecp_vdp_vsitypeid,
391 			{ "VSI type ID", "ecp.vdp.vsitypeid", FT_UINT24, BASE_HEX,
392 				NULL, 0x0, NULL, HFILL },
393 		},
394 		{ &hf_ecp_vdp_vsitypeidversion,
395 			{ "VSI type ID version", "ecp.vdp.vsitypeidversion", FT_UINT8, BASE_HEX,
396 				NULL, 0x0, NULL, HFILL },
397 		},
398 		{ &hf_ecp_vdp_instanceid,
399 			{ "VSI Instance ID version", "ecp.vdp.instanceid", FT_BYTES, BASE_NONE,
400 				NULL, 0x0, NULL, HFILL },
401 		},
402 		{ &hf_ecp_vdp_format,
403 			{ "VSI filter info format", "ecp.vdp.format", FT_UINT8, BASE_HEX,
404 				VALS(ecp_vdp_formats), 0x0, NULL, HFILL },
405 		},
406 		{ &hf_ecp_vdp_mac,
407 			{ "VSI Mac Address", "ecp.vdp.mac", FT_ETHER, BASE_NONE,
408 				NULL, 0x0, NULL, HFILL }
409 		},
410 		{ &hf_ecp_vdp_vlan,
411 			{ "VSI VLAN ID", "ecp.vdp.vlan", FT_UINT16, BASE_DEC,
412 				NULL, 0x0, NULL, HFILL }
413 		},
414 	};
415 
416 	static gint *ett[] = {
417 		&ett_ecp,
418 		&ett_end_of_vdpdu,
419 		&ett_802_1qbg_capabilities_flags,
420 	};
421 
422 	proto_ecp = proto_register_protocol("ECP Protocol", "ECP", "ecp");
423 	proto_register_field_array(proto_ecp, hf, array_length(hf));
424 	proto_register_subtree_array(ett, array_length(ett));
425 
426 	ieee802a_add_oui(OUI_IEEE_802_1QBG, "ieee802a.ecp_pid",
427 		"IEEE802a ECP PID", &hf_reg, proto_ecp);
428 
429 	ecp_handle = register_dissector("ecp", dissect_ecp, proto_ecp);
430 }
431 
proto_reg_handoff_ecp(void)432 void proto_reg_handoff_ecp(void)
433 {
434 	dissector_add_uint("ieee802a.ecp_pid", 0x0000, ecp_handle);
435 }
436 
437 /*
438  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
439  *
440  * Local variables:
441  * c-basic-offset: 8
442  * tab-width: 8
443  * indent-tabs-mode: t
444  * End:
445  *
446  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
447  * :indentSize=8:tabSize=8:noTabs=false:
448  */
449