From e5e7981a10699f0af2ffea4c0e0f542e447b2e4a Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Fri, 6 Aug 2021 08:01:12 -0400 Subject: [PATCH] unix: add ifreq and ifreqData helpers for Linux ifreq is difficult to use in Go due to the union field in particular. This situation is made worse due to the need to comply with Go's unsafe.Pointer rules. This CL generates the raw ifreq type and also adds an ifreqData type of the same size which is specialized for use with unsafe.Pointer. We also replace the existing ifreqEthtool (which was not padded to the correct size) with the new APIs and add a test to verify that IoctlGetEthtoolDrvinfo functions properly by checking the name of the driver for each network interface. Future uses of ifreq in package unix can expand upon this type with additional getter and setter methods to deal with the unsafe casts to and from the union byte array. We may also consider exporting ifreq and ifreqData if necessary. Change-Id: Ibf73a10e774b4336815c674bb867bbb7ec1b9c71 Reviewed-on: https://go-review.googlesource.com/c/sys/+/340369 Run-TryBot: Matt Layher Trust: Matt Layher Reviewed-by: Ian Lance Taylor TryBot-Result: Go Bot --- unix/ifreq_linux.go | 48 +++++++++++++++++++++++++++++ unix/ioctl_linux.go | 25 +++++---------- unix/linux/types.go | 2 ++ unix/syscall_internal_linux_test.go | 8 +++++ unix/syscall_linux_test.go | 34 ++++++++++++++++++++ unix/ztypes_linux_386.go | 5 +++ unix/ztypes_linux_amd64.go | 5 +++ unix/ztypes_linux_arm.go | 5 +++ unix/ztypes_linux_arm64.go | 5 +++ unix/ztypes_linux_mips.go | 5 +++ unix/ztypes_linux_mips64.go | 5 +++ unix/ztypes_linux_mips64le.go | 5 +++ unix/ztypes_linux_mipsle.go | 5 +++ unix/ztypes_linux_ppc.go | 5 +++ unix/ztypes_linux_ppc64.go | 5 +++ unix/ztypes_linux_ppc64le.go | 5 +++ unix/ztypes_linux_riscv64.go | 5 +++ unix/ztypes_linux_s390x.go | 5 +++ unix/ztypes_linux_sparc64.go | 5 +++ 19 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 unix/ifreq_linux.go diff --git a/unix/ifreq_linux.go b/unix/ifreq_linux.go new file mode 100644 index 00000000..cb07859f --- /dev/null +++ b/unix/ifreq_linux.go @@ -0,0 +1,48 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package unix + +import "unsafe" + +// Helpers for dealing with ifreq since it contains a union and thus requires a +// lot of unsafe.Pointer casts to use properly. + +// newIfreq creates an ifreq with the input network interface name after +// validating the name does not exceed IFNAMSIZ-1 (trailing NULL required) +// bytes. +func newIfreq(name string) (*ifreq, error) { + // Leave room for terminating NULL byte. + if len(name) >= IFNAMSIZ { + return nil, EINVAL + } + + var ifr ifreq + copy(ifr.Ifrn[:], name) + + return &ifr, nil +} + +// An ifreqData is an ifreq but with a typed unsafe.Pointer field for data in +// the union. This is required in order to comply with the unsafe.Pointer rules +// since the "pointer-ness" of data would not be preserved if it were cast into +// the byte array of a raw ifreq. +type ifreqData struct { + name [IFNAMSIZ]byte + data unsafe.Pointer + // Pad to the same size as ifreq. + _ [len(ifreq{}.Ifru) - SizeofPtr]byte +} + +// SetData produces an ifreqData with the pointer p set for ioctls which require +// arbitrary pointer data. +func (ifr ifreq) SetData(p unsafe.Pointer) ifreqData { + return ifreqData{ + name: ifr.Ifrn, + data: p, + } +} diff --git a/unix/ioctl_linux.go b/unix/ioctl_linux.go index 48773f73..7e4f64af 100644 --- a/unix/ioctl_linux.go +++ b/unix/ioctl_linux.go @@ -50,28 +50,19 @@ func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error { return err } -type ifreqEthtool struct { - name [IFNAMSIZ]byte - data unsafe.Pointer -} - // IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network // device specified by ifname. func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) { - // Leave room for terminating NULL byte. - if len(ifname) >= IFNAMSIZ { - return nil, EINVAL + ifr, err := newIfreq(ifname) + if err != nil { + return nil, err } - value := EthtoolDrvinfo{ - Cmd: ETHTOOL_GDRVINFO, - } - ifreq := ifreqEthtool{ - data: unsafe.Pointer(&value), - } - copy(ifreq.name[:], ifname) - err := ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifreq))) - runtime.KeepAlive(ifreq) + value := EthtoolDrvinfo{Cmd: ETHTOOL_GDRVINFO} + ifrd := ifr.SetData(unsafe.Pointer(&value)) + + err = ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifrd))) + runtime.KeepAlive(ifrd) return &value, err } diff --git a/unix/linux/types.go b/unix/linux/types.go index 41081f2d..cc884adf 100644 --- a/unix/linux/types.go +++ b/unix/linux/types.go @@ -634,6 +634,8 @@ type TCPInfo C.struct_tcp_info type CanFilter C.struct_can_filter +type ifreq C.struct_ifreq + const ( SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go index 7ec21ca7..9ca15fcd 100644 --- a/unix/syscall_internal_linux_test.go +++ b/unix/syscall_internal_linux_test.go @@ -14,6 +14,14 @@ import ( "unsafe" ) +func Test_ifreqSize(t *testing.T) { + // Ensure ifreq (generated) and ifreqData (hand-written due to + // unsafe.Pointer field) are identical in size. + if want, got := unsafe.Sizeof(ifreq{}), unsafe.Sizeof(ifreqData{}); want != got { + t.Fatalf("unexpected ifreq size: got: %d, want: %d", got, want) + } +} + func makeProto(proto int) *int { return &proto } diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go index ccc6f199..fe96bbee 100644 --- a/unix/syscall_linux_test.go +++ b/unix/syscall_linux_test.go @@ -13,6 +13,7 @@ import ( "errors" "fmt" "io/ioutil" + "net" "os" "path/filepath" "runtime" @@ -26,6 +27,39 @@ import ( "golang.org/x/sys/unix" ) +func TestIoctlGetEthtoolDrvinfo(t *testing.T) { + if runtime.GOOS == "android" { + t.Skip("ethtool driver info is not available on android, skipping test") + } + + s, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("failed to open socket: %v", err) + } + defer unix.Close(s) + + ifis, err := net.Interfaces() + if err != nil { + t.Fatalf("failed to get network interfaces: %v", err) + } + + // Print the interface name and associated driver information for each + // network interface supported by ethtool. + for _, ifi := range ifis { + drv, err := unix.IoctlGetEthtoolDrvinfo(s, ifi.Name) + if err != nil { + if err == unix.EOPNOTSUPP { + continue + } + + t.Fatalf("failed to get ethtool driver info for %q: %v", ifi.Name, err) + } + + // Trim trailing NULLs. + t.Logf("%s: %q", ifi.Name, string(bytes.TrimRight(drv.Driver[:], "\x00"))) + } +} + func TestIoctlGetInt(t *testing.T) { f, err := os.Open("/dev/random") if err != nil { diff --git a/unix/ztypes_linux_386.go b/unix/ztypes_linux_386.go index 235c62e4..f4a435f6 100644 --- a/unix/ztypes_linux_386.go +++ b/unix/ztypes_linux_386.go @@ -630,3 +630,8 @@ const ( PPS_GETCAP = 0x800470a3 PPS_FETCH = 0xc00470a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [16]byte +} diff --git a/unix/ztypes_linux_amd64.go b/unix/ztypes_linux_amd64.go index 99b1e5b6..a8996dae 100644 --- a/unix/ztypes_linux_amd64.go +++ b/unix/ztypes_linux_amd64.go @@ -648,3 +648,8 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_arm.go b/unix/ztypes_linux_arm.go index cc8bba79..e9d9632f 100644 --- a/unix/ztypes_linux_arm.go +++ b/unix/ztypes_linux_arm.go @@ -625,3 +625,8 @@ const ( PPS_GETCAP = 0x800470a3 PPS_FETCH = 0xc00470a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [16]byte +} diff --git a/unix/ztypes_linux_arm64.go b/unix/ztypes_linux_arm64.go index fa8fe3a7..fbf5270e 100644 --- a/unix/ztypes_linux_arm64.go +++ b/unix/ztypes_linux_arm64.go @@ -627,3 +627,8 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_mips.go b/unix/ztypes_linux_mips.go index e7fb8d9b..bc6992f6 100644 --- a/unix/ztypes_linux_mips.go +++ b/unix/ztypes_linux_mips.go @@ -631,3 +631,8 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [16]byte +} diff --git a/unix/ztypes_linux_mips64.go b/unix/ztypes_linux_mips64.go index 2fa61d59..9163f884 100644 --- a/unix/ztypes_linux_mips64.go +++ b/unix/ztypes_linux_mips64.go @@ -630,3 +630,8 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_mips64le.go b/unix/ztypes_linux_mips64le.go index 7f363993..7ab4cba5 100644 --- a/unix/ztypes_linux_mips64le.go +++ b/unix/ztypes_linux_mips64le.go @@ -630,3 +630,8 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_mipsle.go b/unix/ztypes_linux_mipsle.go index f3c20cb8..f44f3190 100644 --- a/unix/ztypes_linux_mipsle.go +++ b/unix/ztypes_linux_mipsle.go @@ -631,3 +631,8 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [16]byte +} diff --git a/unix/ztypes_linux_ppc.go b/unix/ztypes_linux_ppc.go index 885d2795..0d586a8d 100644 --- a/unix/ztypes_linux_ppc.go +++ b/unix/ztypes_linux_ppc.go @@ -637,3 +637,8 @@ const ( PPS_GETCAP = 0x400470a3 PPS_FETCH = 0xc00470a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [16]byte +} diff --git a/unix/ztypes_linux_ppc64.go b/unix/ztypes_linux_ppc64.go index a94eb8e1..9ed158c3 100644 --- a/unix/ztypes_linux_ppc64.go +++ b/unix/ztypes_linux_ppc64.go @@ -637,3 +637,8 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_ppc64le.go b/unix/ztypes_linux_ppc64le.go index 659e32eb..7e299ce3 100644 --- a/unix/ztypes_linux_ppc64le.go +++ b/unix/ztypes_linux_ppc64le.go @@ -637,3 +637,8 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_riscv64.go b/unix/ztypes_linux_riscv64.go index ab8ec604..60e71be7 100644 --- a/unix/ztypes_linux_riscv64.go +++ b/unix/ztypes_linux_riscv64.go @@ -655,3 +655,8 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_s390x.go b/unix/ztypes_linux_s390x.go index 3ec08237..351911b7 100644 --- a/unix/ztypes_linux_s390x.go +++ b/unix/ztypes_linux_s390x.go @@ -651,3 +651,8 @@ const ( PPS_GETCAP = 0x800870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} diff --git a/unix/ztypes_linux_sparc64.go b/unix/ztypes_linux_sparc64.go index 23d47447..7ca16135 100644 --- a/unix/ztypes_linux_sparc64.go +++ b/unix/ztypes_linux_sparc64.go @@ -632,3 +632,8 @@ const ( PPS_GETCAP = 0x400870a3 PPS_FETCH = 0xc00870a4 ) + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +}