diff --git a/link.go b/link.go index fc5f376..4efd32c 100644 --- a/link.go +++ b/link.go @@ -31,6 +31,7 @@ type LinkAttrs struct { MasterIndex int // must be the index of a bridge Namespace interface{} // nil | NsPid | NsFd Alias string + Statistics *LinkStatistics } // NewLinkAttrs returns LinkAttrs structure filled with default values @@ -40,6 +41,35 @@ func NewLinkAttrs() LinkAttrs { } } +/* +Ref: struct rtnl_link_stats {...} +*/ +type LinkStatistics struct { + RxPackets uint32 + TxPackets uint32 + RxBytes uint32 + TxBytes uint32 + RxErrors uint32 + TxErrors uint32 + RxDropped uint32 + TxDropped uint32 + Multicast uint32 + Collisions uint32 + RxLengthErrors uint32 + RxOverErrors uint32 + RxCrcErrors uint32 + RxFrameErrors uint32 + RxFifoErrors uint32 + RxMissedErrors uint32 + TxAbortedErrors uint32 + TxCarrierErrors uint32 + TxFifoErrors uint32 + TxHeartbeatErrors uint32 + TxWindowErrors uint32 + RxCompressed uint32 + TxCompressed uint32 +} + // Device links cannot be created via netlink. These links // are links created by udev like 'lo' and 'etho0' type Device struct { diff --git a/link_linux.go b/link_linux.go index 605e9dd..0ed307d 100644 --- a/link_linux.go +++ b/link_linux.go @@ -12,6 +12,8 @@ import ( "github.com/vishvananda/netlink/nl" ) +const SizeofLinkStats = 0x5c + var native = nl.NativeEndian() var lookupByDump = false @@ -943,6 +945,8 @@ func linkDeserialize(m []byte) (Link, error) { base.TxQLen = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_IFALIAS: base.Alias = string(attr.Value[:len(attr.Value)-1]) + case syscall.IFLA_STATS: + base.Statistics = parseLinkStats(attr.Value[:]) } } // Links that don't have IFLA_INFO_KIND are hardware devices @@ -1360,3 +1364,7 @@ func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) { } } } + +func parseLinkStats(data []byte) *LinkStatistics { + return (*LinkStatistics)(unsafe.Pointer(&data[0:SizeofLinkStats][0])) +} diff --git a/link_test.go b/link_test.go index 1d0f811..b431acf 100644 --- a/link_test.go +++ b/link_test.go @@ -779,3 +779,51 @@ func TestLinkSubscribe(t *testing.T) { t.Fatal("Del update not received as expected") } } + +func TestLinkStats(t *testing.T) { + defer setUpNetlinkTest(t)() + + // Create a veth pair and verify the cross-stats once both + // ends are brought up and some ICMPv6 packets are exchanged + v0 := "v0" + v1 := "v1" + + vethLink := &Veth{LinkAttrs: LinkAttrs{Name: v0}, PeerName: v1} + if err := LinkAdd(vethLink); err != nil { + t.Fatal(err) + } + + veth0, err := LinkByName(v0) + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(veth0); err != nil { + t.Fatal(err) + } + + veth1, err := LinkByName(v1) + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(veth1); err != nil { + t.Fatal(err) + } + + time.Sleep(2 * time.Second) + + // verify statistics + veth0, err = LinkByName(v0) + if err != nil { + t.Fatal(err) + } + veth1, err = LinkByName(v1) + if err != nil { + t.Fatal(err) + } + v0Stats := veth0.Attrs().Statistics + v1Stats := veth1.Attrs().Statistics + if v0Stats.RxPackets != v1Stats.TxPackets || v0Stats.TxPackets != v1Stats.RxPackets || + v0Stats.RxBytes != v1Stats.TxBytes || v0Stats.TxBytes != v1Stats.RxBytes { + t.Fatalf("veth ends counters differ:\n%v\n%v", v0Stats, v1Stats) + } +}