Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */