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. >> 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