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 +}