Previously fdb/show required exactly one bridge argument. This change allows specifying multiple bridge names in a single call, reducing the number of unixctl round-trips for clients that need to poll FDB entries across many bridges (e.g. EVPN agents managing multiple VNIs).
For text output, each bridge's output is prefixed with a "bridge <name>" header line when multiple bridges are specified. Single-bridge output remains unchanged for backward compatibility. For JSON output, multiple bridges produce an array of objects with "bridge" and "entries" keys. Single-bridge output remains a flat array of FDB entries. Signed-off-by: Takeru Hayasaka <[email protected]> --- NEWS | 5 +++- ofproto/ofproto-dpif.c | 57 ++++++++++++++++++++++++++++++++++-------- tests/ofproto-dpif.at | 32 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 1a3044cbfb2f..88d3ac7dad97 100644 --- a/NEWS +++ b/NEWS @@ -3,7 +3,10 @@ Post-v3.7.0 - Userspace datapath: * ARP/ND lookups for native tunnel are now rate limited. The holdout timer can be configured with 'tnl/neigh/retrans_time'. - + - ovs-appctl: + * "fdb/show" now accepts multiple bridge names in a single call, + reducing the number of unixctl round-trips for clients that poll + FDB entries across many bridges. v3.7.0 - 16 Feb 2026 -------------------- diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index a02afe8ef335..ff3fe2cf9ee5 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -6242,25 +6242,60 @@ done_unlock: } static void -ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, - const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) +ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) { - const struct ofproto_dpif *ofproto = ofproto_dpif_lookup_by_name(argv[1]); + bool multi = argc > 2; - if (!ofproto) { - unixctl_command_reply_error(conn, "no such bridge"); - return; + /* Validate all bridge names up front. */ + for (int i = 1; i < argc; i++) { + if (!ofproto_dpif_lookup_by_name(argv[i])) { + unixctl_command_reply_error(conn, "no such bridge"); + return; + } } if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) { - struct json *fdb_entries; + struct json **bridge_entries = xmalloc((argc - 1) + * sizeof *bridge_entries); + + for (int i = 1; i < argc; i++) { + const struct ofproto_dpif *ofproto = + ofproto_dpif_lookup_by_name(argv[i]); + struct json *fdb_entries; + + ofproto_unixctl_fdb_show_json(ofproto, &fdb_entries); + if (multi) { + struct json *obj = json_object_create(); - ofproto_unixctl_fdb_show_json(ofproto, &fdb_entries); - unixctl_command_reply_json(conn, fdb_entries); + json_object_put_string(obj, "bridge", argv[i]); + json_object_put(obj, "entries", fdb_entries); + bridge_entries[i - 1] = obj; + } else { + bridge_entries[i - 1] = fdb_entries; + } + } + + if (multi) { + unixctl_command_reply_json( + conn, json_array_create(bridge_entries, argc - 1)); + } else { + unixctl_command_reply_json(conn, bridge_entries[0]); + free(bridge_entries); + } } else { struct ds ds = DS_EMPTY_INITIALIZER; - ofproto_unixctl_fdb_show_text(ofproto, &ds); + for (int i = 1; i < argc; i++) { + const struct ofproto_dpif *ofproto = + ofproto_dpif_lookup_by_name(argv[i]); + + if (multi) { + ds_put_format(&ds, "bridge %s\n", argv[i]); + } + ofproto_unixctl_fdb_show_text(ofproto, &ds); + } + unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } @@ -7131,7 +7166,7 @@ ofproto_unixctl_init(void) ofproto_unixctl_fdb_delete, NULL); unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); - unixctl_command_register("fdb/show", "bridge", 1, 1, + unixctl_command_register("fdb/show", "bridge [bridge2] ...", 1, INT_MAX, ofproto_unixctl_fdb_show, NULL); unixctl_command_register("fdb/stats-clear", "[bridge]", 0, 1, ofproto_unixctl_fdb_stats_clear, NULL); diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 39e43d376849..3d8c5fa1e2f4 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -7639,6 +7639,38 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d 5 0 50:54:00:00:00:07 ? ]) +# Test fdb/show with multiple bridges. +AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 br1 | sed '/^ /s/[[0-9]]\{1,\}$/?/'], [0], [dnl +bridge br0 + port VLAN MAC Age + 2 0 50:54:00:00:00:05 ? +bridge br1 + port VLAN MAC Age + 5 0 50:54:00:00:00:07 ? +]) + +dnl Check json output with multiple bridges. +AT_CHECK([ovs-appctl --format json --pretty fdb/show br0 br1 \ + | sed 's/"age": [[0-9]]*/"age": ?/g'], [0], [dnl +[[ + { + "bridge": "br0", + "entries": [ + { + "age": ?, + "mac": "50:54:00:00:00:05", + "port": 2, + "vlan": 0}]}, + { + "bridge": "br1", + "entries": [ + { + "age": ?, + "mac": "50:54:00:00:00:07", + "port": 5, + "vlan": 0}]}]] +]) + OVS_VSWITCHD_STOP AT_CLEANUP -- 2.43.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
