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 73
/
raw_linux.go
147 lines (120 loc) · 3.61 KB
/
raw_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
//go:build linux
// +build linux
package raw
import (
"net"
"sync/atomic"
"time"
"github.com/mdlayher/packet"
"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
)
// Must implement net.PacketConn at compile-time.
var _ net.PacketConn = &packetConn{}
// packetConn is the Linux-specific implementation of net.PacketConn for this
// package.
type packetConn struct {
ifi *net.Interface
c *packet.Conn
// Should stats be accumulated instead of reset on each call?
noCumulativeStats bool
// Internal storage for cumulative stats.
stats Stats
}
// listenPacket creates a net.PacketConn which can be used to send and receive
// data at the device driver level.
func listenPacket(ifi *net.Interface, proto uint16, cfg Config) (*packetConn, error) {
typ := packet.Raw
if cfg.LinuxSockDGRAM {
typ = packet.Datagram
}
c, err := packet.Listen(ifi, typ, int(proto), &packet.Config{
// Propagate matching options.
Filter: cfg.Filter,
})
if err != nil {
return nil, err
}
return &packetConn{
ifi: ifi,
c: c,
noCumulativeStats: cfg.NoCumulativeStats,
}, nil
}
// ReadFrom implements the net.PacketConn.ReadFrom method.
func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, addr, err := p.c.ReadFrom(b)
if err != nil {
return n, nil, err
}
raddr := &Addr{HardwareAddr: addr.(*packet.Addr).HardwareAddr}
return n, raddr, nil
}
// WriteTo implements the net.PacketConn.WriteTo method.
func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {
raddr, ok := addr.(*Addr)
if !ok {
return 0, unix.EINVAL
}
paddr := &packet.Addr{HardwareAddr: raddr.HardwareAddr}
return p.c.WriteTo(b, paddr)
}
// Close closes the connection.
func (p *packetConn) Close() error {
return p.c.Close()
}
// LocalAddr returns the local network address.
func (p *packetConn) LocalAddr() net.Addr {
addr := p.c.LocalAddr().(*packet.Addr)
return &Addr{
HardwareAddr: addr.HardwareAddr,
}
}
// SetDeadline implements the net.PacketConn.SetDeadline method.
func (p *packetConn) SetDeadline(t time.Time) error {
return p.c.SetDeadline(t)
}
// SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
func (p *packetConn) SetReadDeadline(t time.Time) error {
return p.c.SetReadDeadline(t)
}
// SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
func (p *packetConn) SetWriteDeadline(t time.Time) error {
return p.c.SetWriteDeadline(t)
}
// SetBPF attaches an assembled BPF program to a raw net.PacketConn.
func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error {
return p.c.SetBPF(filter)
}
// SetPromiscuous enables or disables promiscuous mode on the interface, allowing it
// to receive traffic that is not addressed to the interface.
func (p *packetConn) SetPromiscuous(enable bool) error {
return p.c.SetPromiscuous(enable)
}
// Stats retrieves statistics from the Conn.
func (p *packetConn) Stats() (*Stats, error) {
stats, err := p.c.Stats()
if err != nil {
return nil, err
}
return p.handleStats(stats), nil
}
// handleStats handles creation of Stats structures from *packet.Stats.
func (p *packetConn) handleStats(s *packet.Stats) *Stats {
// Does the caller want instantaneous stats as provided by Linux? If so,
// return the structure directly.
if p.noCumulativeStats {
return &Stats{
Packets: uint64(s.Packets),
Drops: uint64(s.Drops),
}
}
// The caller wants cumulative stats. Add stats with the internal stats
// structure and return a copy of the resulting stats.
packets := atomic.AddUint64(&p.stats.Packets, uint64(s.Packets))
drops := atomic.AddUint64(&p.stats.Drops, uint64(s.Drops))
return &Stats{
Packets: packets,
Drops: drops,
}
}