Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-netlink-ovs_datapath.c
Line
Count
Source
1
/* packet-netlink-ovs_datapath.c
2
 * Routines for Open vSwitch datapath netlink protocol dissection
3
 * Copyright 2026, Red Hat Inc.
4
 * By Timothy Redaelli <tredaelli@redhat.com>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
/* ovs_datapath manages Open vSwitch datapaths via Generic Netlink
14
 *
15
 * Relevant Linux kernel header file:
16
 * include/uapi/linux/openvswitch.h
17
 */
18
19
#include "config.h"
20
21
#include <epan/packet.h>
22
23
#include "packet-netlink.h"
24
25
void proto_register_netlink_ovs_datapath(void);
26
void proto_reg_handoff_netlink_ovs_datapath(void);
27
28
/* from <include/uapi/linux/openvswitch.h> prefixed with WS_ */
29
enum ws_ovs_dp_cmd {
30
  WS_OVS_DP_CMD_UNSPEC,
31
  WS_OVS_DP_CMD_NEW,
32
  WS_OVS_DP_CMD_DEL,
33
  WS_OVS_DP_CMD_GET,
34
  WS_OVS_DP_CMD_SET,
35
};
36
37
enum ws_ovs_dp_attr {
38
  WS_OVS_DP_ATTR_UNSPEC,
39
  WS_OVS_DP_ATTR_NAME,
40
  WS_OVS_DP_ATTR_UPCALL_PID,
41
  WS_OVS_DP_ATTR_STATS,
42
  WS_OVS_DP_ATTR_MEGAFLOW_STATS,
43
  WS_OVS_DP_ATTR_USER_FEATURES,
44
  WS_OVS_DP_ATTR_PAD,
45
  WS_OVS_DP_ATTR_MASKS_CACHE_SIZE,
46
  WS_OVS_DP_ATTR_PER_CPU_PIDS,
47
  WS_OVS_DP_ATTR_IFINDEX,
48
};
49
50
static const value_string ws_ovs_dp_commands_vals[] = {
51
  { WS_OVS_DP_CMD_UNSPEC, "OVS_DP_CMD_UNSPEC" },
52
  { WS_OVS_DP_CMD_NEW,    "OVS_DP_CMD_NEW" },
53
  { WS_OVS_DP_CMD_DEL,    "OVS_DP_CMD_DEL" },
54
  { WS_OVS_DP_CMD_GET,    "OVS_DP_CMD_GET" },
55
  { WS_OVS_DP_CMD_SET,    "OVS_DP_CMD_SET" },
56
  { 0, NULL }
57
};
58
59
static const value_string ws_ovs_dp_attr_vals[] = {
60
  { WS_OVS_DP_ATTR_UNSPEC,    "OVS_DP_ATTR_UNSPEC" },
61
  { WS_OVS_DP_ATTR_NAME,      "OVS_DP_ATTR_NAME" },
62
  { WS_OVS_DP_ATTR_UPCALL_PID,    "OVS_DP_ATTR_UPCALL_PID" },
63
  { WS_OVS_DP_ATTR_STATS,   "OVS_DP_ATTR_STATS" },
64
  { WS_OVS_DP_ATTR_MEGAFLOW_STATS,  "OVS_DP_ATTR_MEGAFLOW_STATS" },
65
  { WS_OVS_DP_ATTR_USER_FEATURES, "OVS_DP_ATTR_USER_FEATURES" },
66
  { WS_OVS_DP_ATTR_PAD,     "OVS_DP_ATTR_PAD" },
67
  { WS_OVS_DP_ATTR_MASKS_CACHE_SIZE,  "OVS_DP_ATTR_MASKS_CACHE_SIZE" },
68
  { WS_OVS_DP_ATTR_PER_CPU_PIDS,    "OVS_DP_ATTR_PER_CPU_PIDS" },
69
  { WS_OVS_DP_ATTR_IFINDEX,   "OVS_DP_ATTR_IFINDEX" },
70
  { 0, NULL }
71
};
72
73
struct netlink_ovs_dp_info {
74
  packet_info *pinfo;
75
};
76
77
static dissector_handle_t netlink_ovs_dp_handle;
78
79
static int proto_netlink_ovs_dp;
80
81
static int hf_ovs_dp_commands;
82
static int hf_ovs_dp_dp_ifindex;
83
static int hf_ovs_dp_attr;
84
static int hf_ovs_dp_name;
85
static int hf_ovs_dp_upcall_pid;
86
static int hf_ovs_dp_stats_n_hit;
87
static int hf_ovs_dp_stats_n_missed;
88
static int hf_ovs_dp_stats_n_lost;
89
static int hf_ovs_dp_stats_n_flows;
90
static int hf_ovs_dp_megaflow_stats_n_mask_hit;
91
static int hf_ovs_dp_megaflow_stats_n_masks;
92
static int hf_ovs_dp_megaflow_stats_n_cache_hit;
93
static int hf_ovs_dp_user_features;
94
static int hf_ovs_dp_user_features_unaligned;
95
static int hf_ovs_dp_user_features_vport_pids;
96
static int hf_ovs_dp_user_features_tc_recirc;
97
static int hf_ovs_dp_user_features_per_cpu;
98
static int hf_ovs_dp_masks_cache_size;
99
static int hf_ovs_dp_ifindex;
100
101
static int ett_ovs_dp;
102
static int ett_ovs_dp_attrs;
103
static int ett_ovs_dp_stats;
104
static int ett_ovs_dp_megaflow_stats;
105
static int ett_ovs_dp_user_features;
106
107
static int * const ovs_dp_user_features_fields[] = {
108
  &hf_ovs_dp_user_features_unaligned,
109
  &hf_ovs_dp_user_features_vport_pids,
110
  &hf_ovs_dp_user_features_tc_recirc,
111
  &hf_ovs_dp_user_features_per_cpu,
112
  NULL
113
};
114
115
static int
116
dissect_ovs_dp_attrs(tvbuff_t *tvb, void *data,
117
  struct packet_netlink_data *nl_data, proto_tree *tree,
118
  int nla_type, int offset, int len)
119
0
{
120
0
  enum ws_ovs_dp_attr type = (enum ws_ovs_dp_attr) nla_type;
121
0
  struct netlink_ovs_dp_info *info = (struct netlink_ovs_dp_info *) data;
122
0
  const uint8_t *str;
123
0
  proto_item *pi;
124
0
  proto_tree *ptree;
125
0
  uint32_t value;
126
127
0
  switch (type) {
128
0
  case WS_OVS_DP_ATTR_NAME:
129
0
    DISSECTOR_ASSERT(info);
130
0
    proto_tree_add_item_ret_string(tree, hf_ovs_dp_name,
131
0
      tvb, offset, len, ENC_ASCII | ENC_NA,
132
0
      info->pinfo->pool, &str);
133
0
    proto_item_append_text(tree, ": %s", str);
134
0
    return 1;
135
136
0
  case WS_OVS_DP_ATTR_UPCALL_PID:
137
0
    proto_tree_add_item_ret_uint(tree, hf_ovs_dp_upcall_pid, tvb,
138
0
      offset, 4, nl_data->encoding, &value);
139
0
    proto_item_append_text(tree, ": %u", value);
140
0
    return 1;
141
142
0
  case WS_OVS_DP_ATTR_STATS:
143
    /* struct ovs_dp_stats: n_hit(u64), n_missed(u64),
144
     * n_lost(u64), n_flows(u64) = 32 bytes */
145
0
    if (len == 32) {
146
0
      int off = offset;
147
0
      pi = proto_tree_add_subtree(tree, tvb, offset, len,
148
0
        ett_ovs_dp_stats, NULL, "Datapath Statistics");
149
0
      ptree = proto_item_add_subtree(pi, ett_ovs_dp_stats);
150
0
      proto_tree_add_item(ptree, hf_ovs_dp_stats_n_hit,
151
0
        tvb, off, 8, nl_data->encoding);
152
0
      off += 8;
153
0
      proto_tree_add_item(ptree, hf_ovs_dp_stats_n_missed,
154
0
        tvb, off, 8, nl_data->encoding);
155
0
      off += 8;
156
0
      proto_tree_add_item(ptree, hf_ovs_dp_stats_n_lost,
157
0
        tvb, off, 8, nl_data->encoding);
158
0
      off += 8;
159
0
      proto_tree_add_item(ptree, hf_ovs_dp_stats_n_flows,
160
0
        tvb, off, 8, nl_data->encoding);
161
0
      return 1;
162
0
    }
163
0
    return 0;
164
165
0
  case WS_OVS_DP_ATTR_MEGAFLOW_STATS:
166
    /* struct ovs_dp_megaflow_stats: n_mask_hit(u64),
167
     * n_masks(u32), pad0(u32), n_cache_hit(u64),
168
     * pad1(u64) = 32 bytes.  Accept >= 16 for forward compat. */
169
0
    if (len >= 16) {
170
0
      int off = offset;
171
0
      pi = proto_tree_add_subtree(tree, tvb, offset, len,
172
0
        ett_ovs_dp_megaflow_stats, NULL, "Megaflow Statistics");
173
0
      ptree = proto_item_add_subtree(pi,
174
0
        ett_ovs_dp_megaflow_stats);
175
0
      proto_tree_add_item(ptree,
176
0
        hf_ovs_dp_megaflow_stats_n_mask_hit,
177
0
        tvb, off, 8, nl_data->encoding);
178
0
      off += 8;
179
0
      proto_tree_add_item(ptree,
180
0
        hf_ovs_dp_megaflow_stats_n_masks,
181
0
        tvb, off, 4, nl_data->encoding);
182
0
      off += 4;
183
0
      off += 4; /* pad0 */
184
0
      if (off + 8 <= offset + len) {
185
0
        proto_tree_add_item(ptree,
186
0
          hf_ovs_dp_megaflow_stats_n_cache_hit,
187
0
          tvb, off, 8, nl_data->encoding);
188
0
      }
189
0
      return 1;
190
0
    }
191
0
    return 0;
192
193
0
  case WS_OVS_DP_ATTR_USER_FEATURES:
194
0
    proto_tree_add_bitmask(tree, tvb, offset,
195
0
      hf_ovs_dp_user_features, ett_ovs_dp_user_features,
196
0
      ovs_dp_user_features_fields, nl_data->encoding);
197
0
    return 1;
198
199
0
  case WS_OVS_DP_ATTR_MASKS_CACHE_SIZE:
200
0
    proto_tree_add_item_ret_uint(tree, hf_ovs_dp_masks_cache_size,
201
0
      tvb, offset, 4, nl_data->encoding, &value);
202
0
    proto_item_append_text(tree, ": %u", value);
203
0
    return 1;
204
205
0
  case WS_OVS_DP_ATTR_PER_CPU_PIDS:
206
    /* Array of u32 PIDs, one per CPU */
207
0
    for (int i = 0; i + 4 <= len; i += 4) {
208
0
      proto_tree_add_item(tree, hf_ovs_dp_upcall_pid, tvb,
209
0
        offset + i, 4, nl_data->encoding);
210
0
    }
211
0
    return 1;
212
213
0
  case WS_OVS_DP_ATTR_IFINDEX:
214
0
    proto_tree_add_item_ret_uint(tree, hf_ovs_dp_ifindex, tvb,
215
0
      offset, 4, nl_data->encoding, &value);
216
0
    proto_item_append_text(tree, ": %u", value);
217
0
    return 1;
218
219
0
  default:
220
0
    return 0;
221
0
  }
222
0
}
223
224
static int
225
dissect_netlink_ovs_datapath(tvbuff_t *tvb, packet_info *pinfo,
226
  proto_tree *tree, void *data)
227
0
{
228
0
  genl_info_t *genl_info = (genl_info_t *) data;
229
0
  struct netlink_ovs_dp_info info;
230
0
  proto_tree *nlmsg_tree;
231
0
  proto_item *pi;
232
0
  int offset;
233
234
0
  DISSECTOR_ASSERT(genl_info);
235
236
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "ovs_datapath");
237
0
  col_clear(pinfo->cinfo, COL_INFO);
238
239
0
  offset = dissect_genl_header(tvb, genl_info, genl_info->nl_data,
240
0
    hf_ovs_dp_commands);
241
242
  /* OVS header (dp_ifindex) */
243
0
  if (tvb_reported_length_remaining(tvb, offset) < 4)
244
0
    return offset;
245
246
0
  pi = proto_tree_add_item(tree, proto_netlink_ovs_dp, tvb, offset,
247
0
    -1, ENC_NA);
248
0
  nlmsg_tree = proto_item_add_subtree(pi, ett_ovs_dp);
249
250
0
  proto_tree_add_item(nlmsg_tree, hf_ovs_dp_dp_ifindex, tvb, offset,
251
0
    4, genl_info->nl_data->encoding);
252
0
  offset += 4;
253
254
0
  if (!tvb_reported_length_remaining(tvb, offset))
255
0
    return offset;
256
257
0
  info.pinfo = pinfo;
258
0
  offset = dissect_netlink_attributes_to_end(tvb, hf_ovs_dp_attr,
259
0
    ett_ovs_dp_attrs, &info, genl_info->nl_data,
260
0
    nlmsg_tree, offset, dissect_ovs_dp_attrs);
261
262
0
  return offset;
263
0
}
264
265
void
266
proto_register_netlink_ovs_datapath(void)
267
16
{
268
16
  static hf_register_info hf[] = {
269
16
    { &hf_ovs_dp_commands,
270
16
      { "Command", "ovs_datapath.cmd",
271
16
        FT_UINT8, BASE_DEC,
272
16
        VALS(ws_ovs_dp_commands_vals), 0x00,
273
16
        NULL, HFILL }
274
16
    },
275
16
    { &hf_ovs_dp_dp_ifindex,
276
16
      { "Datapath ifindex", "ovs_datapath.dp_ifindex",
277
16
        FT_INT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
278
16
    },
279
16
    { &hf_ovs_dp_attr,
280
16
      { "Attribute type", "ovs_datapath.attr_type",
281
16
        FT_UINT16, BASE_DEC, VALS(ws_ovs_dp_attr_vals),
282
16
        NLA_TYPE_MASK, NULL, HFILL }
283
16
    },
284
16
    { &hf_ovs_dp_name,
285
16
      { "Name", "ovs_datapath.name",
286
16
        FT_STRINGZ, BASE_NONE, NULL, 0x00, NULL, HFILL }
287
16
    },
288
16
    { &hf_ovs_dp_upcall_pid,
289
16
      { "Upcall PID", "ovs_datapath.upcall_pid",
290
16
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
291
16
    },
292
16
    { &hf_ovs_dp_stats_n_hit,
293
16
      { "Flows hit", "ovs_datapath.stats.n_hit",
294
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
295
16
    },
296
16
    { &hf_ovs_dp_stats_n_missed,
297
16
      { "Flows missed", "ovs_datapath.stats.n_missed",
298
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
299
16
    },
300
16
    { &hf_ovs_dp_stats_n_lost,
301
16
      { "Flows lost", "ovs_datapath.stats.n_lost",
302
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
303
16
    },
304
16
    { &hf_ovs_dp_stats_n_flows,
305
16
      { "Number of flows", "ovs_datapath.stats.n_flows",
306
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
307
16
    },
308
16
    { &hf_ovs_dp_megaflow_stats_n_mask_hit,
309
16
      { "Mask hits",
310
16
        "ovs_datapath.megaflow_stats.n_mask_hit",
311
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
312
16
    },
313
16
    { &hf_ovs_dp_megaflow_stats_n_masks,
314
16
      { "Number of masks",
315
16
        "ovs_datapath.megaflow_stats.n_masks",
316
16
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
317
16
    },
318
16
    { &hf_ovs_dp_megaflow_stats_n_cache_hit,
319
16
      { "Cache hits",
320
16
        "ovs_datapath.megaflow_stats.n_cache_hit",
321
16
        FT_UINT64, BASE_DEC, NULL, 0x00, NULL, HFILL }
322
16
    },
323
16
    { &hf_ovs_dp_user_features,
324
16
      { "User features", "ovs_datapath.user_features",
325
16
        FT_UINT32, BASE_HEX, NULL, 0x00, NULL, HFILL }
326
16
    },
327
16
    { &hf_ovs_dp_user_features_unaligned,
328
16
      { "Unaligned NL attributes",
329
16
        "ovs_datapath.user_features.unaligned",
330
16
        FT_BOOLEAN, 32, NULL, 0x00000001, NULL, HFILL }
331
16
    },
332
16
    { &hf_ovs_dp_user_features_vport_pids,
333
16
      { "Multiple PIDs per vport",
334
16
        "ovs_datapath.user_features.vport_pids",
335
16
        FT_BOOLEAN, 32, NULL, 0x00000002, NULL, HFILL }
336
16
    },
337
16
    { &hf_ovs_dp_user_features_tc_recirc,
338
16
      { "TC offload recirc sharing",
339
16
        "ovs_datapath.user_features.tc_recirc",
340
16
        FT_BOOLEAN, 32, NULL, 0x00000004, NULL, HFILL }
341
16
    },
342
16
    { &hf_ovs_dp_user_features_per_cpu,
343
16
      { "Per-CPU upcall dispatch",
344
16
        "ovs_datapath.user_features.per_cpu",
345
16
        FT_BOOLEAN, 32, NULL, 0x00000008, NULL, HFILL }
346
16
    },
347
16
    { &hf_ovs_dp_masks_cache_size,
348
16
      { "Masks cache size",
349
16
        "ovs_datapath.masks_cache_size",
350
16
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
351
16
    },
352
16
    { &hf_ovs_dp_ifindex,
353
16
      { "Interface index", "ovs_datapath.ifindex",
354
16
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
355
16
    },
356
16
  };
357
358
16
  static int *ett[] = {
359
16
    &ett_ovs_dp,
360
16
    &ett_ovs_dp_attrs,
361
16
    &ett_ovs_dp_stats,
362
16
    &ett_ovs_dp_megaflow_stats,
363
16
    &ett_ovs_dp_user_features,
364
16
  };
365
366
16
  proto_netlink_ovs_dp = proto_register_protocol(
367
16
    "Linux ovs_datapath (Open vSwitch Datapath) protocol",
368
16
    "ovs_datapath", "ovs_datapath");
369
16
  proto_register_field_array(proto_netlink_ovs_dp, hf,
370
16
    array_length(hf));
371
16
  proto_register_subtree_array(ett, array_length(ett));
372
373
16
  netlink_ovs_dp_handle = register_dissector("ovs_datapath",
374
16
    dissect_netlink_ovs_datapath, proto_netlink_ovs_dp);
375
16
}
376
377
void
378
proto_reg_handoff_netlink_ovs_datapath(void)
379
16
{
380
16
  dissector_add_string("genl.family", "ovs_datapath",
381
16
    netlink_ovs_dp_handle);
382
16
}
383
384
/*
385
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
386
 *
387
 * Local variables:
388
 * c-basic-offset: 8
389
 * tab-width: 8
390
 * indent-tabs-mode: t
391
 * End:
392
 *
393
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
394
 * :indentSize=8:tabSize=8:noTabs=false:
395
 */