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,
}
}
|