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