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