On Tue, Apr 14, 2026 at 1:04 PM Takeru Hayasaka <[email protected]> wrote:
> 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); > Why not just add the bridge name in ofproto_unixctl_fdb_show_json? It would simplify this patch and also might simplify parsing of the output. > + 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) { > Why not just always output the bridge name? It would be more consistent and could move the change to ofproto_unixctl_fdb_show_text. Both of these suggested changes moves the output formatting into a single function. Cheers, M > + 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 > > _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
