...

Source file src/github.com/mdlayher/packet/packet_linux.go

Documentation: github.com/mdlayher/packet

     1  //go:build linux
     2  // +build linux
     3  
     4  package packet
     5  
     6  import (
     7  	"context"
     8  	"encoding/binary"
     9  	"errors"
    10  	"math"
    11  	"net"
    12  	"os"
    13  
    14  	"github.com/josharian/native"
    15  	"github.com/mdlayher/socket"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // A conn is the net.PacketConn implementation for packet sockets. We can use
    20  // socket.Conn directly on Linux to implement most of the necessary methods.
    21  type conn = socket.Conn
    22  
    23  // readFrom implements the net.PacketConn ReadFrom method using recvfrom(2).
    24  func (c *Conn) readFrom(b []byte) (int, net.Addr, error) {
    25  	// From net.PacketConn documentation:
    26  	//
    27  	// "[ReadFrom] returns the number of bytes read (0 <= n <= len(p)) and any
    28  	// error encountered. Callers should always process the n > 0 bytes returned
    29  	// before considering the error err."
    30  	//
    31  	// c.opError will return nil if no error, but either way we return all the
    32  	// information that we have.
    33  	n, sa, err := c.c.Recvfrom(context.Background(), b, 0)
    34  	return n, fromSockaddr(sa), c.opError(opRead, err)
    35  }
    36  
    37  // writeTo implements the net.PacketConn WriteTo method.
    38  func (c *Conn) writeTo(b []byte, addr net.Addr) (int, error) {
    39  	sa, err := c.toSockaddr("sendto", addr)
    40  	if err != nil {
    41  		return 0, c.opError(opWrite, err)
    42  	}
    43  
    44  	// TODO(mdlayher): it's curious that unix.Sendto does not return the number
    45  	// of bytes actually sent. Fake it for now, but investigate upstream.
    46  	if err := c.c.Sendto(context.Background(), b, 0, sa); err != nil {
    47  		return 0, c.opError(opWrite, err)
    48  	}
    49  
    50  	return len(b), nil
    51  }
    52  
    53  // setPromiscuous wraps setsockopt(2) for the unix.PACKET_MR_PROMISC option.
    54  func (c *Conn) setPromiscuous(enable bool) error {
    55  	mreq := unix.PacketMreq{
    56  		Ifindex: int32(c.ifIndex),
    57  		Type:    unix.PACKET_MR_PROMISC,
    58  	}
    59  
    60  	membership := unix.PACKET_DROP_MEMBERSHIP
    61  	if enable {
    62  		membership = unix.PACKET_ADD_MEMBERSHIP
    63  	}
    64  
    65  	return c.opError(
    66  		opSetsockopt,
    67  		c.c.SetsockoptPacketMreq(unix.SOL_PACKET, membership, &mreq),
    68  	)
    69  }
    70  
    71  // stats wraps getsockopt(2) for tpacket_stats* types.
    72  func (c *Conn) stats() (*Stats, error) {
    73  	const (
    74  		level = unix.SOL_PACKET
    75  		name  = unix.PACKET_STATISTICS
    76  	)
    77  
    78  	// Try to fetch V3 statistics first, they contain more detailed information.
    79  	if stats, err := c.c.GetsockoptTpacketStatsV3(level, name); err == nil {
    80  		return &Stats{
    81  			Packets:          stats.Packets,
    82  			Drops:            stats.Drops,
    83  			FreezeQueueCount: stats.Freeze_q_cnt,
    84  		}, nil
    85  	}
    86  
    87  	// There was an error fetching v3 stats, try to fall back.
    88  	stats, err := c.c.GetsockoptTpacketStats(level, name)
    89  	if err != nil {
    90  		return nil, c.opError(opGetsockopt, err)
    91  	}
    92  
    93  	return &Stats{
    94  		Packets: stats.Packets,
    95  		Drops:   stats.Drops,
    96  		// FreezeQueueCount is not present.
    97  	}, nil
    98  }
    99  
   100  // listen is the entry point for Listen on Linux.
   101  func listen(ifi *net.Interface, socketType Type, protocol int, cfg *Config) (*Conn, error) {
   102  	if cfg == nil {
   103  		// Default configuration.
   104  		cfg = &Config{}
   105  	}
   106  
   107  	// Convert Type to the matching SOCK_* constant.
   108  	var typ int
   109  	switch socketType {
   110  	case Raw:
   111  		typ = unix.SOCK_RAW
   112  	case Datagram:
   113  		typ = unix.SOCK_DGRAM
   114  	default:
   115  		return nil, errors.New("packet: invalid Type value")
   116  	}
   117  
   118  	// Protocol is intentionally zero in call to socket(2); we can set it on
   119  	// bind(2) instead. Package raw notes: "Do not specify a protocol to avoid
   120  	// capturing packets which to not match cfg.Filter."
   121  	c, err := socket.Socket(unix.AF_PACKET, typ, 0, network, nil)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	conn, err := bind(c, ifi.Index, protocol, cfg)
   127  	if err != nil {
   128  		_ = c.Close()
   129  		return nil, err
   130  	}
   131  
   132  	return conn, nil
   133  }
   134  
   135  // bind binds the *socket.Conn to finalize *Conn setup.
   136  func bind(c *socket.Conn, ifIndex, protocol int, cfg *Config) (*Conn, error) {
   137  	if len(cfg.Filter) > 0 {
   138  		// The caller wants to apply a BPF filter before bind(2).
   139  		if err := c.SetBPF(cfg.Filter); err != nil {
   140  			return nil, err
   141  		}
   142  	}
   143  
   144  	// packet(7) says we sll_protocol must be in network byte order.
   145  	pnet, err := htons(protocol)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	// TODO(mdlayher): investigate the possibility of sll_ifindex = 0 because we
   151  	// could bind to any interface.
   152  	err = c.Bind(&unix.SockaddrLinklayer{
   153  		Protocol: pnet,
   154  		Ifindex:  ifIndex,
   155  	})
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	lsa, err := c.Getsockname()
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	// Parse the physical layer address; sll_halen tells us how many bytes of
   166  	// sll_addr we should treat as valid.
   167  	lsall := lsa.(*unix.SockaddrLinklayer)
   168  	addr := make(net.HardwareAddr, lsall.Halen)
   169  	copy(addr, lsall.Addr[:])
   170  
   171  	return &Conn{
   172  		c: c,
   173  
   174  		addr:     &Addr{HardwareAddr: addr},
   175  		ifIndex:  ifIndex,
   176  		protocol: pnet,
   177  	}, nil
   178  }
   179  
   180  // fromSockaddr converts an opaque unix.Sockaddr to *Addr. If sa is nil, it
   181  // returns nil. It panics if sa is not of type *unix.SockaddrLinklayer.
   182  func fromSockaddr(sa unix.Sockaddr) *Addr {
   183  	if sa == nil {
   184  		return nil
   185  	}
   186  
   187  	sall := sa.(*unix.SockaddrLinklayer)
   188  
   189  	return &Addr{
   190  		// The syscall already allocated sa; just slice into it with the
   191  		// appropriate length and type conversion rather than making a copy.
   192  		HardwareAddr: net.HardwareAddr(sall.Addr[:sall.Halen]),
   193  	}
   194  }
   195  
   196  // toSockaddr converts a net.Addr to an opaque unix.Sockaddr. It returns an
   197  // error if the fields cannot be packed into a *unix.SockaddrLinklayer.
   198  func (c *Conn) toSockaddr(
   199  	op string,
   200  	addr net.Addr,
   201  ) (unix.Sockaddr, error) {
   202  	// The typical error convention for net.Conn types is
   203  	// net.OpError(os.SyscallError(syscall.Errno)), so all calls here should
   204  	// return os.SyscallError(syscall.Errno) so the caller can apply the final
   205  	// net.OpError wrapper.
   206  
   207  	// Ensure the correct Addr type.
   208  	a, ok := addr.(*Addr)
   209  	if !ok || a.HardwareAddr == nil {
   210  		return nil, os.NewSyscallError(op, unix.EINVAL)
   211  	}
   212  
   213  	// Pack Addr and Conn metadata into the appropriate sockaddr fields. From
   214  	// packet(7):
   215  	//
   216  	// "When you send packets it is enough to specify sll_family, sll_addr,
   217  	// sll_halen, sll_ifindex, and sll_protocol. The other fields should be 0."
   218  	//
   219  	// sll_family is set on the conversion to unix.RawSockaddrLinklayer.
   220  	sa := unix.SockaddrLinklayer{
   221  		Ifindex:  c.ifIndex,
   222  		Protocol: c.protocol,
   223  	}
   224  
   225  	// Ensure the input address does not exceed the amount of space available;
   226  	// for example an IPoIB address is 20 bytes.
   227  	if len(a.HardwareAddr) > len(sa.Addr) {
   228  		return nil, os.NewSyscallError(op, unix.EINVAL)
   229  	}
   230  
   231  	sa.Halen = uint8(len(a.HardwareAddr))
   232  	copy(sa.Addr[:], a.HardwareAddr)
   233  
   234  	return &sa, nil
   235  }
   236  
   237  // htons converts a short (uint16) from host-to-network byte order.
   238  func htons(i int) (uint16, error) {
   239  	if i < 0 || i > math.MaxUint16 {
   240  		return 0, errors.New("packet: protocol value out of range")
   241  	}
   242  
   243  	// Store as big endian, retrieve as native endian.
   244  	var b [2]byte
   245  	binary.BigEndian.PutUint16(b[:], uint16(i))
   246  
   247  	return native.Endian.Uint16(b[:]), nil
   248  }
   249  

View as plain text