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         | 52 ++++++++++++++++++++++++++++++++++++++
 tests/system-interface.at  | 48 +++++++++++++++++++++++++++++++++++
 vswitchd/vswitch.xml       | 13 ++++++++++
 4 files changed, 114 insertions(+)

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 a68e3e79a..034421045 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -3735,6 +3735,51 @@ netdev_linux_get_next_hop(const struct in_addr *host, 
struct in_addr *next_hop,
     return ENXIO;
 }
 
+/* For veth devices, query peer_ifindex via ethtool statistics. */
+static void
+netdev_linux_get_peer_ifindex(struct netdev_linux *netdev)
+{
+    struct ethtool_gstrings *names = NULL;
+    struct ethtool_stats *stats = NULL;
+    size_t n_stats = netdev->drvinfo.n_stats;
+    struct ethtool_cmd *cmd;
+    int error;
+
+    if (strcmp(netdev->drvinfo.driver, "veth") || !n_stats) {
+        return;
+    }
+
+    error = netdev_linux_read_definitions(netdev, ETH_SS_STATS, &names);
+    if (error) {
+        return;
+    }
+
+    stats = xzalloc(sizeof *stats + n_stats * sizeof stats->data[0]);
+    stats->cmd = ETHTOOL_GSTATS;
+    stats->n_stats = n_stats;
+
+    cmd = (struct ethtool_cmd *) stats;
+    error = netdev_linux_do_ethtool(netdev->up.name, cmd,
+                                    ETHTOOL_GSTATS, "ETHTOOL_GSTATS");
+    if (error) {
+        free(stats);
+        free(names);
+        return;
+    }
+
+    for (uint32_t i = 0; i < names->len && i < stats->n_stats; i++) {
+        char *name = (char *) &names->data[i * ETH_GSTRING_LEN];
+
+        if (!strcmp(name, "peer_ifindex")) {
+            netdev->peer_ifindex = stats->data[i];
+            break;
+        }
+    }
+
+    free(stats);
+    free(names);
+}
+
 int
 netdev_linux_get_status(const struct netdev *netdev_, struct smap *smap)
 {
@@ -3747,11 +3792,13 @@ 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) {
+            netdev_linux_get_peer_ifindex(netdev);
             netdev->cache_valid |= VALID_DRVINFO;
         }
     }
@@ -3760,6 +3807,11 @@ 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);
+
+        if (netdev->peer_ifindex) {
+            smap_add_format(smap, "peer_ifindex",
+                            "%"PRIu64, netdev->peer_ifindex);
+        }
     }
     ovs_mutex_unlock(&netdev->mutex);
 
diff --git a/tests/system-interface.at b/tests/system-interface.at
index 20a882d1c..89e36809e 100644
--- a/tests/system-interface.at
+++ b/tests/system-interface.at
@@ -231,3 +231,51 @@ AT_CHECK([grep 'name too long' stderr], [0], [ignore])
 
 OVS_TRAFFIC_VSWITCHD_STOP(["/could not open network device.*name too long/d"])
 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()
+
+ADD_NAMESPACES(testns)
+
+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])
+on_exit 'ip link del ovs-veth0'
+AT_CHECK([ip link set ovs-veth1 netns testns])
+
+AT_CHECK([ovs-vsctl add-port br0 ovs-veth0])
+
+dnl Get the actual ifindex of ovs-veth1 in testns.
+AT_CHECK([ip netns exec testns ip link show ovs-veth1], [0], [stdout])
+peer1_ifindex=$(sed -n 's/^\([[0-9]]*\):.*/\1/p' stdout)
+
+dnl Verify OVS reports the correct peer_ifindex.
+OVS_WAIT_UNTIL_EQUAL([ovs-vsctl get interface ovs-veth0 status:peer_ifindex],
+                     ["\"$peer1_ifindex\""])
+
+dnl Recreate veth pair with the same name to test stale ifindex detection.
+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.
+AT_CHECK([ip netns exec testns ip link show ovs-veth1], [0], [stdout])
+peer2_ifindex=$(sed -n 's/^\([[0-9]]*\):.*/\1/p' stdout)
+
+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\""])
+
+dnl Verify the two peer ifindexes are different.
+AT_CHECK([test "$peer1_ifindex" != "$peer2_ifindex"])
+
+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>.
-- 
2.43.0

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

Reply via email to