]> git.feebdaed.xyz Git - 0xmirror/containerd.git/commitdiff
cri: populate Network.Interfaces in PodSandboxStats on Linux
authorDavanum Srinivas <davanum@gmail.com>
Fri, 19 Dec 2025 23:04:20 +0000 (18:04 -0500)
committerDavanum Srinivas <davanum@gmail.com>
Sat, 20 Dec 2025 00:40:17 +0000 (19:40 -0500)
Added getAllContainerNetIO() to collect stats for all interfaces in
the pod's network namespace.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
internal/cri/server/sandbox_stats_linux.go

index 2bd96bf65b979643da7b70cab88cbc3ad7db499b..65ed106a69444a785e1a6e8d66cb901536b5af0d 100644 (file)
@@ -79,20 +79,37 @@ func (c *criService) podSandboxStats(
        podSandboxStats.Linux.Memory = memoryStats
 
        if sandbox.NetNSPath != "" {
-               linkStats, err := getContainerNetIO(ctx, sandbox.NetNSPath)
+               allStats, err := getAllContainerNetIO(ctx, sandbox.NetNSPath)
                if err != nil {
                        return nil, fmt.Errorf("failed to obtain network stats: %w", err)
                }
-               podSandboxStats.Linux.Network = &runtime.NetworkUsage{
+
+               networkUsage := &runtime.NetworkUsage{
                        Timestamp: timestamp.UnixNano(),
-                       DefaultInterface: &runtime.NetworkInterfaceUsage{
-                               Name:     defaultIfName,
-                               RxBytes:  &runtime.UInt64Value{Value: linkStats.RxBytes},
-                               RxErrors: &runtime.UInt64Value{Value: linkStats.RxErrors},
-                               TxBytes:  &runtime.UInt64Value{Value: linkStats.TxBytes},
-                               TxErrors: &runtime.UInt64Value{Value: linkStats.TxErrors},
-                       },
                }
+
+               for _, ifStats := range allStats {
+                       interfaceUsage := &runtime.NetworkInterfaceUsage{
+                               Name:     ifStats.name,
+                               RxBytes:  &runtime.UInt64Value{Value: ifStats.stats.RxBytes},
+                               RxErrors: &runtime.UInt64Value{Value: ifStats.stats.RxErrors},
+                               TxBytes:  &runtime.UInt64Value{Value: ifStats.stats.TxBytes},
+                               TxErrors: &runtime.UInt64Value{Value: ifStats.stats.TxErrors},
+                       }
+                       networkUsage.Interfaces = append(networkUsage.Interfaces, interfaceUsage)
+
+                       // Set the default interface to eth0, or the first interface if eth0 is not found
+                       if ifStats.name == defaultIfName {
+                               networkUsage.DefaultInterface = interfaceUsage
+                       }
+               }
+
+               // If we didn't find eth0, use the first interface as default
+               if networkUsage.DefaultInterface == nil && len(networkUsage.Interfaces) > 0 {
+                       networkUsage.DefaultInterface = networkUsage.Interfaces[0]
+               }
+
+               podSandboxStats.Linux.Network = networkUsage
        }
 
        listContainerStatsRequest := &runtime.ListContainerStatsRequest{Filter: &runtime.ContainerStatsFilter{PodSandboxId: meta.ID}}
@@ -113,6 +130,13 @@ func (c *criService) podSandboxStats(
        return podSandboxStats, nil
 }
 
+// interfaceStats contains statistics for a single network interface
+type interfaceStats struct {
+       name  string
+       stats netlink.LinkStatistics64
+}
+
+// getContainerNetIO returns network stats for the default interface (eth0)
 // https://github.com/cri-o/cri-o/blob/74a5cf8dffd305b311eb1c7f43a4781738c388c1/internal/oci/stats.go#L32
 func getContainerNetIO(ctx context.Context, netNsPath string) (netlink.LinkStatistics64, error) {
        var stats netlink.LinkStatistics64
@@ -132,6 +156,39 @@ func getContainerNetIO(ctx context.Context, netNsPath string) (netlink.LinkStati
        return stats, err
 }
 
+// getAllContainerNetIO returns network stats for all interfaces in the network namespace
+func getAllContainerNetIO(ctx context.Context, netNsPath string) ([]interfaceStats, error) {
+       var allStats []interfaceStats
+       err := ns.WithNetNSPath(netNsPath, func(_ ns.NetNS) error {
+               links, err := netlink.LinkList()
+               if err != nil {
+                       log.G(ctx).WithError(err).Errorf("unable to list network interfaces for netNsPath: %v", netNsPath)
+                       return err
+               }
+               for _, link := range links {
+                       attrs := link.Attrs()
+                       if attrs == nil {
+                               continue
+                       }
+                       // Skip loopback interface
+                       if attrs.Name == "lo" {
+                               continue
+                       }
+                       var stats netlink.LinkStatistics64
+                       if attrs.Statistics != nil {
+                               stats = netlink.LinkStatistics64(*attrs.Statistics)
+                       }
+                       allStats = append(allStats, interfaceStats{
+                               name:  attrs.Name,
+                               stats: stats,
+                       })
+               }
+               return nil
+       })
+
+       return allStats, err
+}
+
 func cgroupMetricsForSandbox(sandbox sandboxstore.Sandbox) (*cgroupMetrics, error) {
        cgroupPath := sandbox.Config.GetLinux().GetCgroupParent()
        if cgroupPath == "" {