...

Source file src/github.com/mdlayher/packet/packet_linux_test.go

Documentation: github.com/mdlayher/packet

     1  //go:build go1.16
     2  // +build go1.16
     3  
     4  // Just because the library builds and runs on older versions of Go doesn't mean
     5  // we have to apply the same restrictions for tests. Go 1.16 is the oldest
     6  // upstream supported version of Go as of February 2022.
     7  
     8  package packet_test
     9  
    10  import (
    11  	"encoding/binary"
    12  	"errors"
    13  	"net"
    14  	"os"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/mdlayher/packet"
    19  	"golang.org/x/sys/unix"
    20  )
    21  
    22  func TestConnListen(t *testing.T) {
    23  	// Open a connection on an Ethernet interface and begin listening for
    24  	// incoming Ethernet frames. We assume that this interface will receive some
    25  	// sort of traffic in the next 30 seconds and we will capture that traffic
    26  	// by looking for any EtherType value (ETH_P_ALL).
    27  	c, ifi := testConn(t)
    28  	t.Logf("interface: %q, MTU: %d", ifi.Name, ifi.MTU)
    29  
    30  	if err := c.SetReadDeadline(time.Now().Add(30 * time.Second)); err != nil {
    31  		t.Fatalf("failed to set read deadline: %v", err)
    32  	}
    33  
    34  	b := make([]byte, ifi.MTU)
    35  	n, addr, err := c.ReadFrom(b)
    36  	if err != nil {
    37  		t.Fatalf("failed to read Ethernet frame: %v", err)
    38  	}
    39  
    40  	// Received some data, assume some Stats were populated.
    41  	stats, err := c.Stats()
    42  	if err != nil {
    43  		t.Fatalf("failed to fetch stats: %v", err)
    44  	}
    45  	if stats.Packets == 0 {
    46  		t.Fatal("stats indicated 0 received packets")
    47  	}
    48  
    49  	t.Logf("  - packets: %d, drops: %d, freeze queue count: %d",
    50  		stats.Packets, stats.Drops, stats.FreezeQueueCount)
    51  
    52  	// TODO(mdlayher): we could import github.com/mdlayher/ethernet, but parsing
    53  	// an Ethernet frame header is fairly easy and this keeps the go.mod tidy.
    54  
    55  	// Need at least destination MAC, source MAC, and EtherType.
    56  	const header = 6 + 6 + 2
    57  	if n < header {
    58  		t.Fatalf("did not read a complete Ethernet frame from %v, only %d bytes read",
    59  			addr, n)
    60  	}
    61  
    62  	// Parse the header to provide tidy log output.
    63  	var (
    64  		dst = net.HardwareAddr(b[0:6])
    65  		src = net.HardwareAddr(b[6:12])
    66  		et  = binary.BigEndian.Uint16(b[12:14])
    67  	)
    68  
    69  	// Check for the most likely EtherType values.
    70  	var ets string
    71  	switch et {
    72  	case 0x0800:
    73  		ets = "IPv4"
    74  	case 0x0806:
    75  		ets = "ARP"
    76  	case 0x86dd:
    77  		ets = "IPv6"
    78  	default:
    79  		ets = "unknown"
    80  	}
    81  
    82  	// And finally print what we found for the user.
    83  	t.Log("Ethernet frame:")
    84  	t.Logf("  - destination: %s", dst)
    85  	t.Logf("  -      source: %s", src)
    86  	t.Logf("  -   ethertype: %#04x (%s)", et, ets)
    87  	t.Logf("  -     payload: %d bytes", n-header)
    88  }
    89  
    90  // testConn produces a *packet.Conn bound to the returned *net.Interface. The
    91  // caller does not need to call Close on the *packet.Conn.
    92  func testConn(t *testing.T) (*packet.Conn, *net.Interface) {
    93  	t.Helper()
    94  
    95  	// TODO(mdlayher): probably parameterize the EtherType.
    96  	ifi := testInterface(t)
    97  	c, err := packet.Listen(ifi, packet.Raw, unix.ETH_P_ALL, nil)
    98  	if err != nil {
    99  		if errors.Is(err, os.ErrPermission) {
   100  			t.Skipf("skipping, permission denied (try setting CAP_NET_RAW capability): %v", err)
   101  		}
   102  
   103  		t.Fatalf("failed to listen: %v", err)
   104  	}
   105  
   106  	t.Cleanup(func() { c.Close() })
   107  	return c, ifi
   108  }
   109  
   110  // testInterface looks for a suitable Ethernet interface to bind a *packet.Conn.
   111  func testInterface(t *testing.T) *net.Interface {
   112  	ifis, err := net.Interfaces()
   113  	if err != nil {
   114  		t.Fatalf("failed to get network interfaces: %v", err)
   115  	}
   116  
   117  	if len(ifis) == 0 {
   118  		t.Skip("skipping, no network interfaces found")
   119  	}
   120  
   121  	// Try to find a suitable network interface for tests.
   122  	var tried []string
   123  	for _, ifi := range ifis {
   124  		tried = append(tried, ifi.Name)
   125  
   126  		// true is used to line up other checks.
   127  		ok := true &&
   128  			// Look for an Ethernet interface.
   129  			len(ifi.HardwareAddr) == 6 &&
   130  			// Look for up, multicast, broadcast.
   131  			ifi.Flags&(net.FlagUp|net.FlagMulticast|net.FlagBroadcast) != 0
   132  
   133  		if ok {
   134  			return &ifi
   135  		}
   136  	}
   137  
   138  	t.Skipf("skipping, could not find a usable network interface, tried: %s", tried)
   139  	panic("unreachable")
   140  }
   141  

View as plain text