/src/wireshark/epan/dissectors/packet-pw-eth.c
Line | Count | Source |
1 | | /* packet-pw-eth.c |
2 | | * Routines for ethernet PW dissection: it should conform to RFC 4448. |
3 | | * |
4 | | * Copyright 2008 _FF_ |
5 | | * |
6 | | * Francesco Fondelli <francesco dot fondelli, gmail dot com> |
7 | | * |
8 | | * Wireshark - Network traffic analyzer |
9 | | * By Gerald Combs <gerald@wireshark.org> |
10 | | * Copyright 1998 Gerald Combs |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/addr_resolv.h> |
19 | | #include <epan/etypes.h> |
20 | | #include <epan/dissectors/packet-llc.h> |
21 | | |
22 | | #include "packet-mpls.h" |
23 | | |
24 | | void proto_register_pw_eth(void); |
25 | | void proto_reg_handoff_pw_eth(void); |
26 | | |
27 | | static int proto_pw_eth_cw; |
28 | | static int proto_pw_eth_nocw; |
29 | | static int proto_pw_eth_heuristic; |
30 | | |
31 | | static int ett_pw_eth; |
32 | | |
33 | | static int hf_pw_eth; |
34 | | static int hf_pw_eth_cw; |
35 | | static int hf_pw_eth_cw_sequence_number; |
36 | | |
37 | | static dissector_handle_t eth_withoutfcs_handle; |
38 | | static dissector_handle_t pw_eth_handle_cw; |
39 | | static dissector_handle_t pw_eth_handle_nocw; |
40 | | static dissector_handle_t pw_eth_handle_heuristic; |
41 | | |
42 | | static int |
43 | | dissect_pw_eth_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
44 | 1.31k | { |
45 | 1.31k | tvbuff_t *next_tvb; |
46 | 1.31k | uint16_t sequence_number; |
47 | | |
48 | 1.31k | if (tvb_reported_length_remaining(tvb, 0) < 4) { |
49 | 94 | return 0; |
50 | 94 | } |
51 | | |
52 | 1.21k | if (dissect_try_cw_first_nibble(tvb, pinfo, tree)) |
53 | 0 | return tvb_captured_length(tvb); |
54 | | |
55 | 1.21k | sequence_number = tvb_get_ntohs(tvb, 2); |
56 | | |
57 | 1.21k | if (tree) { |
58 | 1.21k | proto_tree *pw_eth_tree; |
59 | 1.21k | proto_item *ti; |
60 | | |
61 | 1.21k | ti = proto_tree_add_boolean(tree, hf_pw_eth_cw, |
62 | 1.21k | tvb, 0, 0, true); |
63 | 1.21k | proto_item_set_hidden(ti); |
64 | 1.21k | ti = proto_tree_add_item(tree, proto_pw_eth_cw, |
65 | 1.21k | tvb, 0, 4, ENC_NA); |
66 | 1.21k | pw_eth_tree = proto_item_add_subtree(ti, ett_pw_eth); |
67 | | |
68 | 1.21k | proto_tree_add_uint_format(pw_eth_tree, |
69 | 1.21k | hf_pw_eth_cw_sequence_number, |
70 | 1.21k | tvb, 2, 2, sequence_number, |
71 | 1.21k | "Sequence Number: %d", |
72 | 1.21k | sequence_number); |
73 | 1.21k | } |
74 | | |
75 | 1.21k | next_tvb = tvb_new_subset_remaining(tvb, 4); |
76 | 1.21k | { |
77 | 1.21k | call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree); |
78 | 1.21k | } |
79 | | |
80 | 1.21k | return tvb_captured_length(tvb); |
81 | 1.21k | } |
82 | | |
83 | | static int |
84 | | dissect_pw_eth_nocw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
85 | 439 | { |
86 | 439 | tvbuff_t *next_tvb; |
87 | | |
88 | 439 | if (tree) { |
89 | 439 | proto_item *ti; |
90 | 439 | ti = proto_tree_add_boolean(tree, hf_pw_eth, tvb, 0, 0, true); |
91 | 439 | proto_item_set_hidden(ti); |
92 | 439 | } |
93 | | |
94 | 439 | next_tvb = tvb_new_subset_remaining(tvb, 0); |
95 | 439 | call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree); |
96 | | |
97 | 439 | return tvb_captured_length(tvb); |
98 | 439 | } |
99 | | |
100 | | /* |
101 | | * FF: this function returns true if the first 12 bytes in tvb looks like |
102 | | * two valid ethernet addresses. false otherwise. |
103 | | */ |
104 | | static int |
105 | | looks_like_plain_eth(tvbuff_t *tvb, int offset) |
106 | 3.52k | { |
107 | 3.52k | const char *manuf_name_da; |
108 | 3.52k | const char *manuf_name_sa; |
109 | 3.52k | uint16_t etype; |
110 | 3.52k | int ret = 2; |
111 | | |
112 | | /* Don't throw an exception. If the packet is truncated, you lose. */ |
113 | 3.52k | if (tvb_captured_length_remaining(tvb, offset) < 14) { |
114 | 351 | return 0; |
115 | 351 | } |
116 | | |
117 | | /* Copy the source and destination addresses, as tvb_get_manuf_name_if_known |
118 | | * only uses the first three bytes (it's for an OUI in, e.g., IEEE 802.11), |
119 | | * and returns NULL for MA-M and MA-S. |
120 | | */ |
121 | 3.16k | uint8_t da[6], sa[6]; |
122 | 3.16k | tvb_memcpy(tvb, da, offset, 6); |
123 | | /* da[0] & 0x2 is the U/L bit; if it's set, none of this helps. (#13039) */ |
124 | 3.16k | if (da[0] & 0x2) { |
125 | | // U/L bit; locally assigned addresses are a less solid heuristic |
126 | 1.01k | ret = 1; |
127 | 2.15k | } else { |
128 | 2.15k | manuf_name_da = get_manuf_name_if_known(da, 6); |
129 | 2.15k | if (!manuf_name_da) { |
130 | | /* Try looking for an exact match in the ethers file. */ |
131 | 759 | manuf_name_da = get_ether_name_if_known(da); |
132 | 759 | if (!manuf_name_da) { |
133 | 759 | return 0; |
134 | 759 | } |
135 | 759 | } |
136 | 2.15k | } |
137 | 2.41k | offset += 6; |
138 | | |
139 | 2.41k | tvb_memcpy(tvb, sa, offset, 6); |
140 | 2.41k | if (sa[0] & 0x1) { |
141 | | // Group bit should not be set on source |
142 | 654 | return 0; |
143 | 654 | } |
144 | 1.75k | if (sa[0] & 0x2) { |
145 | | // U/L bit; locally assigned addresses are a less solid heuristic |
146 | 764 | ret = 1; |
147 | 992 | } else { |
148 | 992 | manuf_name_sa = get_manuf_name_if_known(sa, 6); |
149 | 992 | if (!manuf_name_sa) { |
150 | 293 | manuf_name_sa = get_ether_name_if_known(sa); |
151 | 293 | if (!manuf_name_sa) { |
152 | 293 | return 0; |
153 | 293 | } |
154 | 293 | } |
155 | 992 | } |
156 | 1.46k | offset += 6; |
157 | 1.46k | etype = tvb_get_ntohs(tvb, offset); |
158 | | |
159 | 1.46k | if (etype > IEEE_802_3_MAX_LEN) { |
160 | 889 | if (etype < ETHERNET_II_MIN_LEN) { |
161 | 2 | return 0; |
162 | 2 | } |
163 | | |
164 | 887 | if (!try_val_to_str(etype, etype_vals)) { |
165 | 498 | return 0; |
166 | 498 | } |
167 | 887 | } else { |
168 | 574 | offset += 2; |
169 | | /* XXX - There are unusual cases like Cisco ISL, Novell raw 802.3 |
170 | | * for IPX/SPX, etc. See packet-eth capture_eth() |
171 | | */ |
172 | 574 | if (tvb_reported_length_remaining(tvb, offset) < etype) { |
173 | 178 | return 0; |
174 | 178 | } |
175 | | |
176 | 396 | if (tvb_captured_length_remaining(tvb, offset) < 3) { |
177 | 33 | return 0; |
178 | 33 | } |
179 | 363 | uint8_t sap; |
180 | 363 | sap = tvb_get_uint8(tvb, offset); |
181 | 363 | if (!try_val_to_str(sap, sap_vals)) { |
182 | 164 | return 0; |
183 | 164 | } |
184 | 199 | offset += 1; |
185 | 199 | sap = tvb_get_uint8(tvb, offset); |
186 | 199 | if (!try_val_to_str(sap, sap_vals)) { |
187 | 30 | return 0; |
188 | 30 | } |
189 | | /* We could go deeper, and see if this looks like SNAP if the dsap |
190 | | * and ssap are both 0xAA (the common case). |
191 | | */ |
192 | 199 | } |
193 | | |
194 | 558 | return ret; |
195 | 1.46k | } |
196 | | |
197 | | static int |
198 | | dissect_pw_eth_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
199 | 1.44k | { |
200 | | /* |
201 | | * RFC 8469 states that that both ingress and egress SHOULD support the PW |
202 | | * CW, and if they do, the CW MUST be used. So it looks equally likely to |
203 | | * have the CW as not, assume CW. |
204 | | */ |
205 | 1.44k | uint8_t first_nibble = (tvb_get_uint8(tvb, 0) >> 4) & 0x0F; |
206 | | |
207 | 1.44k | if (first_nibble == 0) { |
208 | 1.44k | if (looks_like_plain_eth(tvb, 4) >= looks_like_plain_eth(tvb, 0)) { |
209 | 1.31k | call_dissector(pw_eth_handle_cw, tvb, pinfo, tree); |
210 | 1.31k | } else { |
211 | 133 | call_dissector(pw_eth_handle_nocw, tvb, pinfo, tree); |
212 | 133 | } |
213 | 1.44k | } else { |
214 | 0 | call_dissector(pw_eth_handle_nocw, tvb, pinfo, tree); |
215 | 0 | } |
216 | 1.44k | return tvb_captured_length(tvb); |
217 | 1.44k | } |
218 | | |
219 | | static bool |
220 | | dissect_pw_eth_nocw_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
221 | 630 | { |
222 | 630 | if (!looks_like_plain_eth(tvb, 0)) { |
223 | 324 | return false; |
224 | 324 | } |
225 | 306 | dissect_pw_eth_nocw(tvb, pinfo, tree, data); |
226 | 306 | return true; |
227 | 630 | } |
228 | | |
229 | | void |
230 | | proto_register_pw_eth(void) |
231 | 14 | { |
232 | 14 | static hf_register_info hf[] = { |
233 | 14 | { |
234 | 14 | &hf_pw_eth, |
235 | 14 | { |
236 | 14 | "PW (ethernet)", |
237 | 14 | "pweth", FT_BOOLEAN, |
238 | 14 | BASE_NONE, NULL, 0x0, NULL, HFILL |
239 | 14 | } |
240 | 14 | }, |
241 | 14 | { |
242 | 14 | &hf_pw_eth_cw, |
243 | 14 | { |
244 | 14 | "PW Control Word (ethernet)", |
245 | 14 | "pweth.cw", FT_BOOLEAN, |
246 | 14 | BASE_NONE, NULL, 0x0, NULL, HFILL |
247 | 14 | } |
248 | 14 | }, |
249 | 14 | { |
250 | 14 | &hf_pw_eth_cw_sequence_number, |
251 | 14 | { |
252 | 14 | "PW sequence number (ethernet)", |
253 | 14 | "pweth.cw.sequence_number", FT_UINT16, |
254 | 14 | BASE_DEC, NULL, 0x0, NULL, HFILL |
255 | 14 | } |
256 | 14 | } |
257 | 14 | }; |
258 | | |
259 | 14 | static int *ett[] = { |
260 | 14 | &ett_pw_eth |
261 | 14 | }; |
262 | | |
263 | 14 | proto_pw_eth_cw = |
264 | 14 | proto_register_protocol("PW Ethernet Control Word", |
265 | 14 | "Ethernet PW (with CW)", |
266 | 14 | "pwethcw"); |
267 | 14 | proto_pw_eth_nocw = |
268 | 14 | proto_register_protocol("Ethernet PW (no CW)", /* not displayed */ |
269 | 14 | "Ethernet PW (no CW)", |
270 | 14 | "pwethnocw"); |
271 | 14 | proto_pw_eth_heuristic = |
272 | 14 | proto_register_protocol("Ethernet PW (CW heuristic)", /* not disp. */ |
273 | 14 | "Ethernet PW (CW heuristic)", |
274 | 14 | "pwethheuristic"); |
275 | 14 | proto_register_field_array(proto_pw_eth_cw, hf, array_length(hf)); |
276 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
277 | 14 | pw_eth_handle_cw = register_dissector("pw_eth_cw", dissect_pw_eth_cw, proto_pw_eth_cw); |
278 | 14 | pw_eth_handle_nocw = register_dissector("pw_eth_nocw", dissect_pw_eth_nocw, proto_pw_eth_nocw); |
279 | 14 | pw_eth_handle_heuristic = register_dissector("pw_eth_heuristic", dissect_pw_eth_heuristic, |
280 | 14 | proto_pw_eth_heuristic); |
281 | 14 | } |
282 | | |
283 | | void |
284 | | proto_reg_handoff_pw_eth(void) |
285 | 14 | { |
286 | 14 | heur_dissector_add("mpls", dissect_pw_eth_nocw_heur, |
287 | 14 | "Ethernet PW (no CW)", "pwethnocw", proto_pw_eth_nocw, |
288 | 14 | HEURISTIC_ENABLE); |
289 | 14 | eth_withoutfcs_handle = find_dissector_add_dependency("eth_withoutfcs", proto_pw_eth_cw); |
290 | | |
291 | 14 | dissector_add_for_decode_as("mpls.label", pw_eth_handle_cw); |
292 | 14 | dissector_add_for_decode_as("mpls.label", pw_eth_handle_nocw); |
293 | | |
294 | 14 | dissector_add_for_decode_as("mpls.label", pw_eth_handle_heuristic); |
295 | | |
296 | 14 | dissector_add_for_decode_as("mpls.pfn", pw_eth_handle_cw); |
297 | 14 | dissector_add_for_decode_as("mpls.pfn", pw_eth_handle_nocw); |
298 | 14 | } |
299 | | |
300 | | /* |
301 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
302 | | * |
303 | | * Local variables: |
304 | | * c-basic-offset: 4 |
305 | | * tab-width: 8 |
306 | | * indent-tabs-mode: nil |
307 | | * End: |
308 | | * |
309 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
310 | | * :indentSize=4:tabSize=8:noTabs=true: |
311 | | */ |