On 30/04/2025 16:43, Eelco Chaudron wrote: > > > On 28 Apr 2025, at 9:32, Eelco Chaudron wrote: > >> On 27 Apr 2025, at 11:59, Roi Dayan wrote: >> >>> On 25/04/2025 14:42, Eelco Chaudron wrote: >>>> >>>> >>>> On 22 Apr 2025, at 15:56, Roi Dayan wrote: >>>> >>>>> From: Dima Chumak <dchu...@nvidia.com> >>>> >>>> Hi Roi/Dima, >>>> >>>> See some more comments below. >>>> >>>> Cheers, >>>> >>>> Eelco >> >> See replies inline. +Ilya to get some feedback on “nexthops” json container. >> >> //Eelco >> >>>> >>>>> The 'ovs/route/show' command now supports machine-readable JSON output >>>>> in addition to the plain-text output for humans. >>>>> Align the keys to match ip route output so also update the command >>>>> help output from mask to mask. >>>>> >>>>> An example json output would be: >>>>> >>>>> ovs-appctl --format json --pretty ovs/route/show >>>>> [ >>>>> { >>>>> "dev": "lo", >>>>> "dst": "::1", >>>>> "local": false, >>>>> "mask": 128, >>>>> "prefsrc": "::1", >>>>> "priority": 128, >>>>> "user": false}, >>>>> { >>>>> "dev": "eth1", >>>>> "dst": "10.237.157.103", >>>>> "local": true, >>>>> "mask": 128, >>>>> "prefsrc": "10.237.157.103", >>>>> "priority": 192, >>>>> "user": false}, >>>>> { >>>>> "dev": "docker0", >>>>> "dst": "fe80::42:67ff:fe28:188", >>>>> "local": true, >>>>> "mask": 128, >>>>> "prefsrc": "fe80::42:67ff:fe28:188", >>>>> "priority": 192, >>>>> "user": false}, >>>>> { >>>>> "dev": "eth0", >>>>> "dst": "0.0.0.0", >>>>> "gw": "192.168.121.1", >>>>> "local": false, >>>>> "mask": 96, >>>>> "prefsrc": "192.168.121.203", >>>>> "priority": 96, >>>>> "user": false}] >>>>> >>>>> Signed-off-by: Dima Chumak <dchu...@nvidia.com> >>>>> Reviewed-by: Roi Dayan <r...@nvidia.com> >>>>> --- >>>>> >>>>> Notes: >>>>> v3 >>>>> - Update ovs-router.at test. >>>>> - Update example output in the commit msg. >>>>> >>>>> v2 >>>>> - Don't prefix boolean with "is_". Use key "local" instead of >>>>> "is_local". >>>>> - Add "user" key. >>>>> - Use ds_clear() in the loop and ds_destroy() outside the loop to >>>>> avoid >>>>> repeated malloc/free. >>>>> - Align json keys to ip route command output. >>>>> >>>>> lib/ovs-router.c | 113 +++++++++++++++++++++++++++++++++++--------- >>>>> tests/ovs-router.at | 41 +++++++++++++++- >>>>> 2 files changed, 131 insertions(+), 23 deletions(-) >>>>> >>>>> diff --git a/lib/ovs-router.c b/lib/ovs-router.c >>>>> index d955a3a543b8..11827bf3c72b 100644 >>>>> --- a/lib/ovs-router.c >>>>> +++ b/lib/ovs-router.c >>>>> @@ -36,6 +36,7 @@ >>>>> #include "dpif.h" >>>>> #include "fatal-signal.h" >>>>> #include "openvswitch/dynamic-string.h" >>>>> +#include "openvswitch/json.h" >>>>> #include "netdev.h" >>>>> #include "packets.h" >>>>> #include "seq.h" >>>>> @@ -429,7 +430,7 @@ ovs_router_add(struct unixctl_conn *conn, int argc, >>>>> is_ipv6 = true; >>>>> } else { >>>>> unixctl_command_reply_error(conn, >>>>> - "Invalid 'ip/plen' parameter"); >>>>> + "Invalid 'ip/mask' parameter"); >>>>> return; >>>>> } >>>>> >>>>> @@ -509,44 +510,112 @@ ovs_router_del(struct unixctl_conn *conn, int argc >>>>> OVS_UNUSED, >>>>> } >>>>> >>>>> static void >>>>> -ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, >>>>> - const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) >>>>> +ovs_router_show_json(struct json **routes) >>>>> { >>>>> + struct json **json_entries = NULL; >>>>> struct ovs_router_entry *rt; >>>>> - struct ds ds = DS_EMPTY_INITIALIZER; >>>>> + struct ds ds; >>>>> + int i = 0; >>>>> >>>>> - ds_put_format(&ds, "Route Table:\n"); >>>>> - CLS_FOR_EACH(rt, cr, &cls) { >>>>> + if (!cls.n_rules) { >>>>> + goto out; >>>>> + } >>>>> + >>>>> + json_entries = xmalloc(cls.n_rules * sizeof *json_entries); >>>>> + >>>>> + CLS_FOR_EACH (rt, cr, &cls) { >>>>> + bool user = rt->priority != rt->plen && !rt->local; >>>>> + struct json *json = json_object_create(); >>>>> + >>>>> + json_object_put(json, "user", json_boolean_create(user)); >>>>> + json_object_put(json, "local", json_boolean_create(rt->local)); >>>>> + json_object_put(json, "priority", >>>>> json_integer_create(rt->priority)); >>>>> + json_object_put(json, "mask", json_integer_create(rt->plen)); >>>> >>>> See my previous comment, the plen needs to be normalized. >>>> if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) plen -= 96. >>>> >>> >>> Hi, It was part of the reply that since this is a json output for scripts >>> we thought to keep it raw data. Also since the output is in a different >>> key prefix_length. unlike the text output showing nw_addr/plen is also a >>> reason maybe to keep this as original plen. thoughts? >> >> The plen is odd because internally we store everything in IPv6 format. So we >> need to adjust it before making it available to the outside world. Or else >> IPv4 addresses will have a prefix of 120 which makes no sense. >> >>>> Also thinking about this again, mask is not the right name, as it’s >>>> actually the prefix length. So I would change it to ‘prefix_length’ >>>> instead. >>>> >>> >>> Ack. Renamed to 'prefix_length' in the json output and I'll revert to the >>> original 'plen' in the command help. >> >> I would just call it prefix in the command help. >> >>>>> + json_object_put_string(json, "dev", rt->output_netdev); >>>>> + >>>>> + ds_init(&ds); >>>> >>>> As you already mentioned this ds_init (and others), need to move outside >>>> the loop, and be replaced with a ds_clear(). >>> >>> Ack. outside a single ds_init() and removed all of the calls from the loop. >>> ds_clear() already exists next to each ds_init() so didn't need a replace. >>> >>>> >>>>> + ipv6_format_mapped(&rt->nw_addr, &ds); >>>>> + json_object_put_string(json, "dst", ds_cstr_ro(&ds)); >>>>> + ds_clear(&ds); >>>> >>>>> + ds_init(&ds); >>>>> + ipv6_format_mapped(&rt->src_addr, &ds); >>>>> + json_object_put_string(json, "prefsrc", ds_cstr_ro(&ds)); >>>>> + ds_clear(&ds); >>>>> + >>>>> + if (rt->mark) { >>>>> + json_object_put(json, "mark", json_integer_create(rt->mark)); >>>>> + } >>>>> + >>>>> + if (ipv6_addr_is_set(&rt->gw)) { >>>>> + ds_init(&ds); >>>>> + ipv6_format_mapped(&rt->gw, &ds); >>>>> + json_object_put_string(json, "gw", ds_cstr_ro(&ds)); >>>>> + ds_clear(&ds); >>>>> + } >>>>> + >>>>> + json_entries[i++] = json; >>>>> + } >>>>> + >>>>> + ds_destroy(&ds); >>>>> + >>>>> +out: >>>>> + *routes = json_array_create(json_entries, i); >>>>> +} >>>>> + >>>>> +static void >>>>> +ovs_router_show_text(struct ds *ds) >>>>> +{ >>>>> + struct ovs_router_entry *rt; >>>>> + >>>>> + ds_put_format(ds, "Route Table:\n"); >>>>> + CLS_FOR_EACH (rt, cr, &cls) { >>>>> uint8_t plen; >>>>> if (rt->priority == rt->plen || rt->local) { >>>>> - ds_put_format(&ds, "Cached: "); >>>>> + ds_put_format(ds, "Cached: "); >>>>> } else { >>>>> - ds_put_format(&ds, "User: "); >>>>> + ds_put_format(ds, "User: "); >>>>> } >>>>> - ipv6_format_mapped(&rt->nw_addr, &ds); >>>>> + ipv6_format_mapped(&rt->nw_addr, ds); >>>>> plen = rt->plen; >>>>> if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) { >>>>> plen -= 96; >>>>> } >>>>> - ds_put_format(&ds, "/%"PRIu8, plen); >>>>> + ds_put_format(ds, "/%"PRIu8, plen); >>>>> if (rt->mark) { >>>>> - ds_put_format(&ds, " MARK %"PRIu32, rt->mark); >>>>> + ds_put_format(ds, " MARK %"PRIu32, rt->mark); >>>>> } >>>>> >>>>> - ds_put_format(&ds, " dev %s", rt->output_netdev); >>>>> + ds_put_format(ds, " dev %s", rt->output_netdev); >>>>> if (ipv6_addr_is_set(&rt->gw)) { >>>>> - ds_put_format(&ds, " GW "); >>>>> - ipv6_format_mapped(&rt->gw, &ds); >>>>> + ds_put_format(ds, " GW "); >>>>> + ipv6_format_mapped(&rt->gw, ds); >>>>> } >>>>> - ds_put_format(&ds, " SRC "); >>>>> - ipv6_format_mapped(&rt->src_addr, &ds); >>>>> + ds_put_format(ds, " SRC "); >>>>> + ipv6_format_mapped(&rt->src_addr, ds); >>>>> if (rt->local) { >>>>> - ds_put_format(&ds, " local"); >>>>> + ds_put_format(ds, " local"); >>>>> } >>>>> - ds_put_format(&ds, "\n"); >>>>> + ds_put_format(ds, "\n"); >>>>> + } >>>>> +} >>>>> + >>>>> +static void >>>>> +ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, >>>>> + const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) >>>>> +{ >>>>> + if (unixctl_command_get_output_format(conn) == >>>>> UNIXCTL_OUTPUT_FMT_JSON) { >>>>> + struct json *routes; >>>>> + >>>>> + ovs_router_show_json(&routes); >>>>> + unixctl_command_reply_json(conn, routes); >>>>> + } else { >>>>> + struct ds ds = DS_EMPTY_INITIALIZER; >>>>> + >>>>> + ovs_router_show_text(&ds); >>>>> + unixctl_command_reply(conn, ds_cstr(&ds)); >>>>> + ds_destroy(&ds); >>>>> } >>>>> - unixctl_command_reply(conn, ds_cstr(&ds)); >>>>> - ds_destroy(&ds); >>>>> } >>>>> >>>>> static void >>>>> @@ -619,12 +688,12 @@ ovs_router_init(void) >>>>> fatal_signal_add_hook(ovs_router_flush_handler, NULL, NULL, >>>>> true); >>>>> classifier_init(&cls, NULL); >>>>> unixctl_command_register("ovs/route/add", >>>>> - "ip/plen output_netdev [gw] " >>>>> + "ip/mask dev [gw] " >>>>> "[pkt_mark=mark] [src=src_ip]", >>>>> 2, 5, ovs_router_add, NULL); >>>>> unixctl_command_register("ovs/route/show", "", 0, 0, >>>>> ovs_router_show, NULL); >>>>> - unixctl_command_register("ovs/route/del", "ip/plen " >>>>> + unixctl_command_register("ovs/route/del", "ip/mask " >>>>> "[pkt_mark=mark]", 1, 2, ovs_router_del, >>>>> NULL); >>>>> unixctl_command_register("ovs/route/lookup", "ip_addr " >>>>> diff --git a/tests/ovs-router.at b/tests/ovs-router.at >>>>> index b3314b3dff0d..6f2378666e7e 100644 >>>>> --- a/tests/ovs-router.at >>>>> +++ b/tests/ovs-router.at >>>>> @@ -16,7 +16,7 @@ Error while inserting route. >>>>> ovs-appctl: ovs-vswitchd: server returned an error >>>>> ]) >>>>> AT_CHECK([ovs-appctl ovs/route/add 1.1.foo.bar/24 br0 2.2.2.10], [2], >>>>> [], [dnl >>>>> -Invalid 'ip/plen' parameter >>>>> +Invalid 'ip/mask' parameter >>>>> ovs-appctl: ovs-vswitchd: server returned an error >>>>> ]) >>>>> AT_CHECK([ovs-appctl ovs/route/add 2.2.2.4/24 br0 pkt_mark=baz], [2], >>>>> [], [dnl >>>>> @@ -28,6 +28,45 @@ User: 1.1.1.0/24 dev br0 GW 2.2.2.10 SRC 2.2.2.2 >>>>> User: 1.1.2.0/24 MARK 2 dev br0 GW 2.2.2.10 SRC 2.2.2.2 >>>>> User: 2.2.2.3/32 MARK 1 dev br0 SRC 2.2.2.2 >>>>> ]) >>>>> +AT_CHECK([ovs-appctl --format=json --pretty ovs/route/show], [0], [dnl >>>>> +[[ >>>>> + { >>>>> + "dev": "br0", >>>>> + "dst": "2.2.2.0", >>>>> + "local": true, >>>>> + "mask": 120, >>>>> + "prefsrc": "2.2.2.2", >>>>> + "priority": 184, >>>>> + "user": false}, >>>>> + { >>>>> + "dev": "br0", >>>>> + "dst": "1.1.1.0", >>>>> + "gw": "2.2.2.10", >>>>> + "local": false, >>>>> + "mask": 120, >>>>> + "prefsrc": "2.2.2.2", >>>>> + "priority": 152, >>>>> + "user": true}, >>>>> + { >>>>> + "dev": "br0", >>>>> + "dst": "1.1.2.0", >>>>> + "gw": "2.2.2.10", >>>>> + "local": false, >>>>> + "mark": 2, >>>>> + "mask": 120, >>>>> + "prefsrc": "2.2.2.2", >>>>> + "priority": 152, >>>>> + "user": true}, >>>>> + { >>>>> + "dev": "br0", >>>>> + "dst": "2.2.2.3", >>>>> + "local": false, >>>>> + "mark": 1, >>>>> + "mask": 128, >>>>> + "prefsrc": "2.2.2.2", >>>>> + "priority": 160, >>>>> + "user": true}]] >>>>> +]) >>>> >>>> I think you missed the following comment (I’ve added a more explanatory >>>> example. Note that gw is changed to gateway): >>>> >>>> Maybe we should also be prepared for multiple next hops, so if a gw is >>>> present, we should do something like the below (for gateway and dev), so >>>> we do not have to change the format later; >>>> >>>> { >>>> "dst": "1.1.1.0", >>>> "local": false, >>>> "mask": 120, >>>> "prefsrc": "2.2.2.2", >>>> "priority": 152, >>>> "user": true, >>>> "nexthops": [ >>>> { >>>> "gateway": "2.2.2.10", >>>> "dev": "br0" >>>> } >>>> ] >>>> } >>>> >>> >>> I thought the comment was general why to keep consistent naming with ip >>> output. >>> If we already wan to match the ip command output then it also doesn't have >>> the >>> nexthops object if it's only a single nexthop. The dev and gateway are at >>> the top >>> level of the json. So only in the future when ovs_router_entry will support >>> multiple nexthops then we should add a nexthops json object to output all >>> nexthops >>> entries. >> >> I think we should introduce the nexthops object now, so the json logic can >> stay the same in future versions. It does not make sense for a json parser >> to differentiate between 1 or multiple nexthops. But let’s get some other >> optinions. Ilya, or others? > > I had a quick offline discussion on this subject, and it would be best to > create a consistent JSON output that is future-proof. Including the next hop > objects in the nexthops JSON container, regardless of how many next hops > exist, would be the best way forward. >
ack. I'll send v5 with all suggestions. thanks. >>> I'll update though 'gw' to 'gateway'. >>> >>> I'll send v4 with all other modifications. >> >> I’d suggest holding off on sending new revisions until the ongoing >> discussions have settled, that way we can avoid too many iterations. >> >>>>> OVS_VSWITCHD_STOP >>>>> AT_CLEANUP >>>>> >>>>> -- >>>>> 2.21.0 >>>> > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev