On 17/04/2025 13:30, Eelco Chaudron wrote:
> 
> 
> On 7 Apr 2025, at 12:23, Roi Dayan via dev wrote:
> 
>> From: Dima Chumak <dchu...@nvidia.com>
>>
>> The 'ovs/route/show' command now supports machine-readable JSON output
>> in addition to the plain-text output for humans. An example would be:
>>
>>   ovs-appctl --format json --pretty ovs/route/show
>>   [
>>     {
>>       "is_local": false,
>>       "nw_addr": "::1",
>>       "output_netdev": "lo",
>>       "plen": 128,
>>       "priority": 128,
>>       "src_addr": "::1"},
>>     {
>>       "is_local": true,
>>       "nw_addr": "10.237.157.103",
>>       "output_netdev": "eth1",
>>       "plen": 128,
>>       "priority": 192,
>>       "src_addr": "10.237.157.103"},
>>     {
>>       "is_local": true,
>>       "nw_addr": "fe80::42:67ff:fe28:188",
>>       "output_netdev": "docker0",
>>       "plen": 128,
>>       "priority": 192,
>>       "src_addr": "fe80::42:67ff:fe28:188"},
>>     {
>>       "gw": "192.168.121.1",
>>       "is_local": false,
>>       "nw_addr": "0.0.0.0",
>>       "output_netdev": "eth0",
>>       "plen": 96,
>>       "priority": 96,
>>       "src_addr": "192.168.121.203"}]
>>
>> Signed-off-by: Dima Chumak <dchu...@nvidia.com>
>> Reviewed-by: Roi Dayan <r...@nvidia.com>
> 
> Hi Dima, thanks for the patch. See some comments below.
> 
> Cheers,
> 
> Eelco
> 
>> ---
>>  lib/ovs-router.c    | 103 ++++++++++++++++++++++++++++++++++++--------
>>  tests/ovs-router.at |  35 +++++++++++++++
>>  2 files changed, 119 insertions(+), 19 deletions(-)
>>
>> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
>> index d955a3a543b8..eb04edece9cb 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"
>> @@ -509,44 +510,108 @@ 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;
>> +    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) {
>> +        struct json *json = json_object_create();
>> +        struct ds ds;
>> +
> 
> In the text output, we show if it’s a user-configured entry; Maybe we should 
> do the same for json? Not sure about the name, but something like:
> 
> json_object_put(json, “user_configured”, json_boolean_create((rt->priority != 
> rt->plen && !rt->local)));

added "user".

> 
>> +        json_object_put(json, "is_local", json_boolean_create(rt->local));

ack.

> 
> I would keep the name "local" as looking at the existing code, we don’t seem 
> to add prefixes for booleans, just the variable name.
> 
>> +        json_object_put(json, "priority", 
>> json_integer_create(rt->priority));
>> +        json_object_put(json, "plen", json_integer_create(rt->plen));
> 
> In the text code the plen is normalized based on the network address type. 
> Should we do the same here?
> Also, the plen is the network mask, so should we add it to the nw_addr 
> instead?
> 

The purpose for json here is for scripts/sw and not user. so "raw" data will be 
easier to handle
instead of parsing the string of ip/mask in the script later.

>> +        json_object_put_string(json, "output_netdev", rt->output_netdev);
>> +
>> +        ds_init(&ds);
>> +        ipv6_format_mapped(&rt->nw_addr, &ds);
>> +        json_object_put_string(json, "nw_addr", ds_cstr_ro(&ds));
>> +        ds_destroy(&ds);
>> +
>> +        ds_init(&ds);
>> +        ipv6_format_mapped(&rt->src_addr, &ds);
>> +        json_object_put_string(json, "src_addr", ds_cstr_ro(&ds));
>> +        ds_destroy(&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_destroy(&ds);
>> +        }
>> +
> 
> Rather than calling ds_destroy() each time, consider calling ds_clear() 
> instead, and only call ds_destroy() once at the end. This avoids repeated 
> malloc/free calls. Preferably move it outside the CLS_FOR_EACH() loop.
> 

ack.

>> +        json_entries[i++] = json;
>> +    }
>> +
>> +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
>> diff --git a/tests/ovs-router.at b/tests/ovs-router.at
>> index b3314b3dff0d..13a38eb55055 100644
>> --- a/tests/ovs-router.at
>> +++ b/tests/ovs-router.at
>> @@ -28,6 +28,41 @@ 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
>> +[[
>> +  {
>> +    "is_local": true,
>> +    "nw_addr": "2.2.2.0",
>> +    "output_netdev": "br0",
>> +    "plen": 120,
>> +    "priority": 184,
>> +    "src_addr": "2.2.2.2"},
> 
> On json naming, should we align a bit more to the ‘ip [—6] -json’ route show 
> output?
> 
> nw_addr -> dst
> src_addr -> prefsrc
> output_netlink -> dev
> 
> 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": "default",
>     "protocol": "ra",
>     "metric": 100,
>     "flags": [],
>     "pref": "medium",
>     "nexthops": [
>       {
>         "gateway": "fe80::4a5a:d01:9431:5120",
>         "dev": "eno8303",
>         "weight": 1,
>         "flags": []
>       },
>       {
>         "gateway": "fe80::4a5a:d01:942f:2b20",
>         "dev": "eno8303",
>         "weight": 1,
>         "flags": []
>       }
>     ]
>   },
> 
> 

It's possible but the help string for ovs/route/add uses the attribute naming.

  ovs/route/add           ip/plen output_netdev [gw] [pkt_mark=mark] 
[src=src_ip]

So this was done to be consistent. do you still prefer to update ? 
if so, do we need to udpate the help print? output_netdev to dev, ip to 
nw_addr, src_ip to src_addr.. ?

>> +  {
>> +    "gw": "2.2.2.10",
>> +    "is_local": false,
>> +    "nw_addr": "1.1.1.0",
>> +    "output_netdev": "br0",
>> +    "plen": 120,
>> +    "priority": 152,
>> +    "src_addr": "2.2.2.2"},
>> +  {
>> +    "gw": "2.2.2.10",
>> +    "is_local": false,
>> +    "mark": 2,
>> +    "nw_addr": "1.1.2.0",
>> +    "output_netdev": "br0",
>> +    "plen": 120,
>> +    "priority": 152,
>> +    "src_addr": "2.2.2.2"},
>> +  {
>> +    "is_local": false,
>> +    "mark": 1,
>> +    "nw_addr": "2.2.2.3",
>> +    "output_netdev": "br0",
>> +    "plen": 128,
>> +    "priority": 160,
>> +    "src_addr": "2.2.2.2"}]]
>> +])
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>
>> -- 
>> 2.21.0
>>
>> _______________________________________________
>> dev mailing list
>> d...@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to