-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathpacket_linux.go
248 lines (209 loc) · 6.6 KB
/
packet_linux.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//go:build linux
// +build linux
package packet
import (
"context"
"encoding/binary"
"errors"
"math"
"net"
"os"
"github.com/josharian/native"
"github.com/mdlayher/socket"
"golang.org/x/sys/unix"
)
// A conn is the net.PacketConn implementation for packet sockets. We can use
// socket.Conn directly on Linux to implement most of the necessary methods.
type conn = socket.Conn
// readFrom implements the net.PacketConn ReadFrom method using recvfrom(2).
func (c *Conn) readFrom(b []byte) (int, net.Addr, error) {
// From net.PacketConn documentation:
//
// "[ReadFrom] returns the number of bytes read (0 <= n <= len(p)) and any
// error encountered. Callers should always process the n > 0 bytes returned
// before considering the error err."
//
// c.opError will return nil if no error, but either way we return all the
// information that we have.
n, sa, err := c.c.Recvfrom(context.Background(), b, 0)
return n, fromSockaddr(sa), c.opError(opRead, err)
}
// writeTo implements the net.PacketConn WriteTo method.
func (c *Conn) writeTo(b []byte, addr net.Addr) (int, error) {
sa, err := c.toSockaddr("sendto", addr)
if err != nil {
return 0, c.opError(opWrite, err)
}
// TODO(mdlayher): it's curious that unix.Sendto does not return the number
// of bytes actually sent. Fake it for now, but investigate upstream.
if err := c.c.Sendto(context.Background(), b, 0, sa); err != nil {
return 0, c.opError(opWrite, err)
}
return len(b), nil
}
// setPromiscuous wraps setsockopt(2) for the unix.PACKET_MR_PROMISC option.
func (c *Conn) setPromiscuous(enable bool) error {
mreq := unix.PacketMreq{
Ifindex: int32(c.ifIndex),
Type: unix.PACKET_MR_PROMISC,
}
membership := unix.PACKET_DROP_MEMBERSHIP
if enable {
membership = unix.PACKET_ADD_MEMBERSHIP
}
return c.opError(
opSetsockopt,
c.c.SetsockoptPacketMreq(unix.SOL_PACKET, membership, &mreq),
)
}
// stats wraps getsockopt(2) for tpacket_stats* types.
func (c *Conn) stats() (*Stats, error) {
const (
level = unix.SOL_PACKET
name = unix.PACKET_STATISTICS
)
// Try to fetch V3 statistics first, they contain more detailed information.
if stats, err := c.c.GetsockoptTpacketStatsV3(level, name); err == nil {
return &Stats{
Packets: stats.Packets,
Drops: stats.Drops,
FreezeQueueCount: stats.Freeze_q_cnt,
}, nil
}
// There was an error fetching v3 stats, try to fall back.
stats, err := c.c.GetsockoptTpacketStats(level, name)
if err != nil {
return nil, c.opError(opGetsockopt, err)
}
return &Stats{
Packets: stats.Packets,
Drops: stats.Drops,
// FreezeQueueCount is not present.
}, nil
}
// listen is the entry point for Listen on Linux.
func listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
if cfg == nil {
// Default configuration.
cfg = &Config{}
}
// Convert Type to the matching SOCK_* constant.
var typ int
switch socketType {
case Raw:
typ = unix.SOCK_RAW
case Datagram:
typ = unix.SOCK_DGRAM
default:
return nil, errors.New("packet: invalid Type value")
}
// Protocol is intentionally zero in call to socket(2); we can set it on
// bind(2) instead. Package raw notes: "Do not specify a protocol to avoid
// capturing packets which to not match cfg.Filter."
c, err := socket.Socket(unix.AF_PACKET, typ, 0, network, nil)
if err != nil {
return nil, err
}
conn, err := bind(c, ifi.Index, protocol, cfg)
if err != nil {
_ = c.Close()
return nil, err
}
return conn, nil
}
// bind binds the *socket.Conn to finalize *Conn setup.
func bind(c *socket.Conn, ifIndex, protocol int, cfg *Config) (*Conn, error) {
if len(cfg.Filter) > 0 {
// The caller wants to apply a BPF filter before bind(2).
if err := c.SetBPF(cfg.Filter); err != nil {
return nil, err
}
}
// packet(7) says we sll_protocol must be in network byte order.
pnet, err := htons(protocol)
if err != nil {
return nil, err
}
// TODO(mdlayher): investigate the possibility of sll_ifindex = 0 because we
// could bind to any interface.
err = c.Bind(&unix.SockaddrLinklayer{
Protocol: pnet,
Ifindex: ifIndex,
})
if err != nil {
return nil, err
}
lsa, err := c.Getsockname()
if err != nil {
return nil, err
}
// Parse the physical layer address; sll_halen tells us how many bytes of
// sll_addr we should treat as valid.
lsall := lsa.(*unix.SockaddrLinklayer)
addr := make(net.HardwareAddr, lsall.Halen)
copy(addr, lsall.Addr[:])
return &Conn{
c: c,
addr: &Addr{HardwareAddr: addr},
ifIndex: ifIndex,
protocol: pnet,
}, nil
}
// fromSockaddr converts an opaque unix.Sockaddr to *Addr. If sa is nil, it
// returns nil. It panics if sa is not of type *unix.SockaddrLinklayer.
func fromSockaddr(sa unix.Sockaddr) *Addr {
if sa == nil {
return nil
}
sall := sa.(*unix.SockaddrLinklayer)
return &Addr{
// The syscall already allocated sa; just slice into it with the
// appropriate length and type conversion rather than making a copy.
HardwareAddr: net.HardwareAddr(sall.Addr[:sall.Halen]),
}
}
// toSockaddr converts a net.Addr to an opaque unix.Sockaddr. It returns an
// error if the fields cannot be packed into a *unix.SockaddrLinklayer.
func (c *Conn) toSockaddr(
op string,
addr net.Addr,
) (unix.Sockaddr, error) {
// The typical error convention for net.Conn types is
// net.OpError(os.SyscallError(syscall.Errno)), so all calls here should
// return os.SyscallError(syscall.Errno) so the caller can apply the final
// net.OpError wrapper.
// Ensure the correct Addr type.
a, ok := addr.(*Addr)
if !ok || a.HardwareAddr == nil {
return nil, os.NewSyscallError(op, unix.EINVAL)
}
// Pack Addr and Conn metadata into the appropriate sockaddr fields. From
// packet(7):
//
// "When you send packets it is enough to specify sll_family, sll_addr,
// sll_halen, sll_ifindex, and sll_protocol. The other fields should be 0."
//
// sll_family is set on the conversion to unix.RawSockaddrLinklayer.
sa := unix.SockaddrLinklayer{
Ifindex: c.ifIndex,
Protocol: c.protocol,
}
// Ensure the input address does not exceed the amount of space available;
// for example an IPoIB address is 20 bytes.
if len(a.HardwareAddr) > len(sa.Addr) {
return nil, os.NewSyscallError(op, unix.EINVAL)
}
sa.Halen = uint8(len(a.HardwareAddr))
copy(sa.Addr[:], a.HardwareAddr)
return &sa, nil
}
// htons converts a short (uint16) from host-to-network byte order.
func htons(i int) (uint16, error) {
if i < 0 || i > math.MaxUint16 {
return 0, errors.New("packet: protocol value out of range")
}
// Store as big endian, retrieve as native endian.
var b [2]byte
binary.BigEndian.PutUint16(b[:], uint16(i))
return native.Endian.Uint16(b[:]), nil
}