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