// 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 ( "bytes" "net" "testing" "unsafe" ) // An ifreqUnion is shorthand for a byte array matching the // architecture-dependent size of an ifreq's union field. type ifreqUnion = [len(ifreq{}.Ifru)]byte func TestNewIfreq(t *testing.T) { // Interface name too long. if _, err := NewIfreq("abcdefghijklmnop"); err != EINVAL { t.Fatalf("expected error EINVAL, but got: %v", err) } } func TestIfreqSize(t *testing.T) { // Ensure ifreq (generated) and Ifreq/ifreqData (hand-written to create a // safe wrapper and store a pointer field) are identical in size. want := unsafe.Sizeof(ifreq{}) if got := unsafe.Sizeof(Ifreq{}); want != got { t.Fatalf("unexpected Ifreq size: got: %d, want: %d", got, want) } if got := unsafe.Sizeof(ifreqData{}); want != got { t.Fatalf("unexpected IfreqData size: got: %d, want: %d", got, want) } } func TestIfreqName(t *testing.T) { // Valid ifreq, expect the hard-coded testIfreq name. ifr := testIfreq(t) if want, got := ifreqName, ifr.Name(); want != got { t.Fatalf("unexpected ifreq name: got: %q, want: %q", got, want) } } func TestIfreqWithData(t *testing.T) { ifr := testIfreq(t) // Store pointer data in the ifreq so we can retrieve it and cast back later // for comparison. want := [5]byte{'h', 'e', 'l', 'l', 'o'} ifrd := ifr.withData(unsafe.Pointer(&want[0])) // Ensure the memory of the original Ifreq was not modified by SetData. if ifr.raw.Ifru != (ifreqUnion{}) { t.Fatalf("ifreq was unexpectedly modified: % #x", ifr.raw.Ifru) } got := *(*[5]byte)(ifrd.data) if want != got { t.Fatalf("unexpected ifreq data bytes:\n got: % #x\nwant: % #x", got, want) } } func TestIfreqInet4Addr(t *testing.T) { ifr := testIfreq(t) in := net.IPv4(192, 0, 2, 1).To4() if err := ifr.SetInet4Addr(in); err != nil { t.Fatalf("failed to set ifreq IPv4 address: %v", err) } // Store fixed offset data (AF_INET, IPv4 address) within underlying // sockaddr bytes. Everything else should be zeroed. want := ifreqUnion{4: 192, 5: 0, 6: 2, 7: 1} if isBigEndian { want[0] = 0x00 want[1] = 0x02 } else { want[0] = 0x02 want[1] = 0x00 } if got := ifr.raw.Ifru; want != got { t.Fatalf("unexpected ifreq sockaddr bytes:\n got: % #x\nwant: % #x", got, want) } got, err := ifr.Inet4Addr() if err != nil { t.Fatalf("failed to get ifreq IPv4 address: %v", err) } if !bytes.Equal(in, got) { t.Fatalf("unexpected ifreq IPv4 address:\n got: % #x\nwant: % #x", got, in) } // Invalid input, wrong length. if err := ifr.SetInet4Addr([]byte{0xff}); err == nil { t.Fatal("expected an error setting invalid IPv4 address, but none occurred") } // Invalid output, AF_INET is only set by SetInet4Addr input. ifr.SetUint32(0xffffffff) if _, err := ifr.Inet4Addr(); err == nil { t.Fatal("expected an error getting invalid IPv4 address, but none occurred") } } func TestIfreqUint16(t *testing.T) { ifr := testIfreq(t) const in = 0x0102 ifr.SetUint16(in) // The layout of the bytes depends on the machine's endianness. var want ifreqUnion if isBigEndian { want[0] = 0x01 want[1] = 0x02 } else { want[0] = 0x02 want[1] = 0x01 } if got := ifr.raw.Ifru; want != got { t.Fatalf("unexpected ifreq uint16 bytes:\n got: % #x\nwant: % #x", got, want) } if got := ifr.Uint16(); in != got { t.Fatalf("unexpected ifreq uint16: got: %d, want: %d", got, in) } } func TestIfreqUint32(t *testing.T) { ifr := testIfreq(t) const in = 0x01020304 ifr.SetUint32(in) // The layout of the bytes depends on the machine's endianness. var want ifreqUnion if isBigEndian { want[0] = 0x01 want[1] = 0x02 want[2] = 0x03 want[3] = 0x04 } else { want[0] = 0x04 want[1] = 0x03 want[2] = 0x02 want[3] = 0x01 } if got := ifr.raw.Ifru; want != got { t.Fatalf("unexpected ifreq uint32 bytes:\n got: % #x\nwant: % #x", got, want) } if got := ifr.Uint32(); in != got { t.Fatalf("unexpected ifreq uint32: got: %d, want: %d", got, in) } } // ifreqName is a hard-coded name for testIfreq. const ifreqName = "eth0" // testIfreq returns an Ifreq with a populated interface name. func testIfreq(t *testing.T) *Ifreq { t.Helper() ifr, err := NewIfreq(ifreqName) if err != nil { t.Fatalf("failed to create ifreq: %v", err) } return ifr }