On 1/27/26 4:11 PM, Matteo Perin via dev wrote:
> This commit adds the peer_ifindex to the interface status column for
> Linux veth devices, allowing applications like OVN to programmatically
> discover veth pairs without requiring additional system calls or manual
> configuration.
> 
> The implementation leverages the existing ethtool infrastructure:
> - Uses ETHTOOL_GDRVINFO to identify veth devices and get n_stats
> - Queries ETH_SS_STATS string set to get statistic names
> - Dynamically allocates buffer based on n_stats
> - Uses ETHTOOL_GSTATS to retrieve veth statistics
> - Finds peer_ifindex by name in the statistics array
> 
> The ifindex value is cached to avoid scalability issues with a
> considerable amount of ports.
> The cached peer_ifindex is then properly invalidated when the
> corresponding veth device is removed.
> This ensures that, when veth pairs are deleted and recreated with
> the same name, the new peer_ifindex is correctly detected and reported.
> 
> A test that create veth pairs across network namespaces, verifies
> peer_ifindex reporting, and validates correct detection of ifindex
> changes when pairs are recreated was added as well.
> 
> Signed-off-by: Matteo Perin <[email protected]>
> ---
>  lib/netdev-linux-private.h |  1 +
>  lib/netdev-linux.c         | 49 ++++++++++++++++++++++++++++++++++++++
>  tests/system-interface.at  | 47 ++++++++++++++++++++++++++++++++++++
>  vswitchd/vswitch.xml       | 13 ++++++++++
>  4 files changed, 110 insertions(+)

Hi, Matteo.  Thanks for the patch!  It seems functionally correct (though
I didn't actually try it).  I left a few comments below, mostly style
and formatting related.

Best regards, Ilya Maximets.

> 
> diff --git a/lib/netdev-linux-private.h b/lib/netdev-linux-private.h
> index cf4a021d3..3f3fc04b5 100644
> --- a/lib/netdev-linux-private.h
> +++ b/lib/netdev-linux-private.h
> @@ -97,6 +97,7 @@ struct netdev_linux {
>      uint8_t current_duplex;          /* Cached from ETHTOOL_GSET. */
>  
>      struct ethtool_drvinfo drvinfo;  /* Cached from ETHTOOL_GDRVINFO. */
> +    uint64_t peer_ifindex;           /* Cached from ETHTOOL_GSTATS (veth). */
>      struct tc *tc;
>  
>      /* For devices of class netdev_tap_class only. */
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 93a064cc2..45779cffa 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -3730,11 +3730,52 @@ netdev_linux_get_status(const struct netdev *netdev_, 
> struct smap *smap)
>  
>          COVERAGE_INC(netdev_get_ethtool);
>          memset(&netdev->drvinfo, 0, sizeof netdev->drvinfo);
> +        netdev->peer_ifindex = 0;
>          error = netdev_linux_do_ethtool(netdev->up.name,
>                                          cmd,
>                                          ETHTOOL_GDRVINFO,
>                                          "ETHTOOL_GDRVINFO");
>          if (!error) {
> +            /* For veth devices, also get the 'peer_ifindex'. */
> +            size_t n_stats = netdev->drvinfo.n_stats;
> +            if (!strcmp(netdev->drvinfo.driver, "veth") &&
> +                n_stats > 0) {

This fits into a single line.  But also there is too much nesting
happening here and it's hard to read.  The content of this if
statement can be moved into a separate function.

Also, while writing the function, please convert all the blocks
like:

if (!error) {
    <do something>
}

into:

if (error) {
    return;
}
<do something>

To save on indentation.

> +                struct ethtool_gstrings *names = NULL;
> +                struct ethtool_stats *stats = NULL;
> +                int s_error;
> +
> +                /* Get the names of the statistics. */
> +                s_error = netdev_linux_read_definitions(netdev,
> +                    ETH_SS_STATS,
> +                    &names);

Indent to the opening parenthesis.

> +                if (!s_error) {
> +                    stats = xzalloc(sizeof *stats +
> +                                    n_stats * sizeof stats->data[0]);
> +                    stats->cmd = ETHTOOL_GSTATS;
> +                    stats->n_stats = n_stats;
> +                    /* Get the statistics values. */
> +                    s_error = netdev_linux_do_ethtool(netdev->up.name,
> +                        (struct ethtool_cmd *) stats,
> +                        ETHTOOL_GSTATS,
> +                        "ETHTOOL_GSTATS");

This should also be indented to the opening parenthesis.  If the cast
doesn't fit, can cast and assing to 'cmd' before the call.

> +                    if (!s_error) {
> +                        /* Find 'peer_ifindex' in names and get value. */
> +                        for (uint32_t i = 0;
> +                            i < names->len && i < stats->n_stats;
> +                            i++) {

Indent to the opening parenthesis, i.e. 1 space to the right.

> +                            char *name =
> +                                (char *) &names->data[i * ETH_GSTRING_LEN];
> +                            if (!strcmp(name, "peer_ifindex") &&
> +                                stats->data[i] > 0) {
> +                                netdev->peer_ifindex = stats->data[i];
> +                                break;
> +                            }
> +                        }
> +                    }
> +                    free(stats);
> +                }
> +                free(names);

No need to free names on error.

> +            }
>              netdev->cache_valid |= VALID_DRVINFO;
>          }
>      }
> @@ -3743,6 +3784,14 @@ netdev_linux_get_status(const struct netdev *netdev_, 
> struct smap *smap)
>          smap_add(smap, "driver_name", netdev->drvinfo.driver);
>          smap_add(smap, "driver_version", netdev->drvinfo.version);
>          smap_add(smap, "firmware_version", netdev->drvinfo.fw_version);
> +
> +        /* Report the cached 'peer_ifindex' (for veth devices). */
> +        if (netdev->peer_ifindex > 0) {

It seems like the value can't be negative in this code, so the
'> 0' part is redundant.

> +            smap_add_format(smap,
> +                "peer_ifindex",
> +                "%"PRIu64,
> +                netdev->peer_ifindex);

This can be two lines instead of 4 - everything on the same line as smap_add
and the last value on its own, indented to the opening parenthesis.

> +        }
>      }
>      ovs_mutex_unlock(&netdev->mutex);
>  
> diff --git a/tests/system-interface.at b/tests/system-interface.at
> index 15c4a0e2e..b24cbc855 100644
> --- a/tests/system-interface.at
> +++ b/tests/system-interface.at
> @@ -221,3 +221,50 @@ half
>  
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
> +
> +dnl Test that peer_ifindex is correctly reported for veth devices.
> +AT_SETUP([interface - veth peer_ifindex status])
> +AT_SKIP_IF([test $HAVE_ETHTOOL = "no"])
> +
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +dnl Create a network namespace for the peer veth end.
> +AT_CHECK([ip netns add testns])
> +on_exit 'ip netns del testns'

We have ADD_NAMESPACES macro that handles everything.

> +
> +dnl Create a veth pair: ovs-veth0 (main ns) <-> ovs-veth1 (testns).
> +AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1])
> +AT_CHECK([ip link set ovs-veth1 netns testns])
> +on_exit 'ip link del ovs-veth0'

This should be right after addition.

> +
> +dnl Add ovs-veth0 to br0.

This is obvious, no need for a comment.

> +AT_CHECK([ovs-vsctl add-port br0 ovs-veth0])
> +
> +dnl Get the actual ifindex of ovs-veth1 in testns and verify it's reported.
> +PEER1_IFINDEX=$(ip netns exec testns ip link show ovs-veth1 | sed -n 
> 's/^\([[0-9]]*\):.*/\1/p')

May be better to use lowercase for shell variables to easier distinguish
them from m4 macros.  Also, might be better to AT_CHECK into stdout and
then parse it.  It would also save some line length.

> +
> +dnl Verify OVS reports the correct peer_ifindex.
> +OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get interface ovs-veth0 
> status:peer_ifindex], ["\"$PEER1_IFINDEX\""])

may split the line after the comma.  Indent to the opening parenthesis.
Also, I didn't try this test, but is it necessary to add those quotes?

> +
> +dnl The veth pair is deleted and then recreated with the same name, this 
> tests ifindex stale values detection.

Line is a bit too long.

> +AT_CHECK([ip link del ovs-veth0])
> +AT_CHECK([ip link add ovs-veth0 type veth peer name ovs-veth1])
> +AT_CHECK([ip link set ovs-veth1 netns testns])
> +
> +dnl Get the NEW peer's ifindex (will be different).
> +PEER2_IFINDEX=$(ip netns exec testns ip link show ovs-veth1 | sed -n 
> 's/^\([[0-9]]*\):.*/\1/p')

Same here.

> +
> +dnl Verify OVS detects the change and reports the NEW peer_ifindex.
> +OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get interface ovs-veth0 
> status:peer_ifindex], ["\"$PEER2_IFINDEX\""])

And here.

> +
> +dnl Verify the two peer ifindexes are different.
> +test "$PEER1_IFINDEX" != "$PEER2_IFINDEX"

This should be under AT_CHECK, otherwise it doesn't really verify much.

> +
> +OVS_TRAFFIC_VSWITCHD_STOP(["dnl
> +/could not open network device ovs-veth0/d
> +/cannot get .*STP status on nonexistent port/d
> +/ethtool command .*on network device ovs-veth0 failed/d
> +/error receiving .*ovs-veth0/d
> +/ovs-veth0: removing policing failed/d"])
> +
> +AT_CLEANUP
> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
> index b7a5afc0a..25f635e82 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -3746,6 +3746,19 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 
> type=patch options:peer=p1 \
>          The version string of the network adapter's firmware, if available.
>        </column>
>  
> +      <column name="status" key="peer_ifindex">
> +        <p>
> +          For veth devices on Linux, this column contains the interface index
> +          of the peer veth device.  This allows applications to
> +          programmatically discover veth pairs without requiring additional
> +          system calls or parsing network interface information.
> +        </p>
> +        <p>
> +          Only present for veth devices (when <ref column="status"
> +          key="driver_name"/> is "veth").  Not available on non-Linux 
> systems.
> +        </p>
> +      </column>
> +
>        <column name="status" key="source_ip">
>          The source IP address used for an IPv4/IPv6 tunnel end-point, such as
>          <code>gre</code>.

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to