| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- // +build linux
- package raw
- import (
- "net"
- "os"
- "sync/atomic"
- "syscall"
- "time"
- "unsafe"
- "golang.org/x/net/bpf"
- "golang.org/x/sys/unix"
- )
- var (
- // Must implement net.PacketConn at compile-time.
- _ net.PacketConn = &packetConn{}
- )
- // packetConn is the Linux-specific implementation of net.PacketConn for this
- // package.
- type packetConn struct {
- ifi *net.Interface
- s socket
- pbe uint16
- // Should stats be accumulated instead of reset on each call?
- noCumulativeStats bool
- // Internal storage for cumulative stats.
- stats Stats
- }
- // socket is an interface which enables swapping out socket syscalls for
- // testing.
- type socket interface {
- Bind(unix.Sockaddr) error
- Close() error
- GetSockoptTpacketStats(level, name int) (*unix.TpacketStats, error)
- Recvfrom([]byte, int) (int, unix.Sockaddr, error)
- Sendto([]byte, int, unix.Sockaddr) error
- SetSockoptPacketMreq(level, name int, mreq *unix.PacketMreq) error
- SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error
- SetDeadline(time.Time) error
- SetReadDeadline(time.Time) error
- SetWriteDeadline(time.Time) error
- }
- // htons converts a short (uint16) from host-to-network byte order.
- // Thanks to mikioh for this neat trick:
- // https://github.com/mikioh/-stdyng/blob/master/afpacket.go
- func htons(i uint16) uint16 {
- return (i<<8)&0xff00 | i>>8
- }
- // 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) {
- filename := "eth-packet-socket"
- // Enabling overriding the socket type via config.
- typ := unix.SOCK_RAW
- if cfg.LinuxSockDGRAM {
- filename = "packet-socket"
- typ = unix.SOCK_DGRAM
- }
- // Open a packet socket using specified socket type. Do not specify
- // a protocol to avoid capturing packets which to not match cfg.Filter.
- // The later call to bind() will set up the correct protocol for us.
- sock, err := unix.Socket(unix.AF_PACKET, typ, 0)
- if err != nil {
- return nil, err
- }
- if err := unix.SetNonblock(sock, true); err != nil {
- return nil, err
- }
- // When using Go 1.12+, the SetNonblock call we just did puts the file
- // descriptor into non-blocking mode. In that case, os.NewFile
- // registers the file descriptor with the runtime poller, which is then
- // used for all subsequent operations.
- //
- // See also: https://golang.org/pkg/os/#NewFile
- f := os.NewFile(uintptr(sock), filename)
- sc, err := f.SyscallConn()
- if err != nil {
- return nil, err
- }
- // Wrap raw socket in socket interface.
- pc, err := newPacketConn(ifi, &sysSocket{f: f, rc: sc}, htons(proto), cfg.Filter)
- if err != nil {
- return nil, err
- }
- pc.noCumulativeStats = cfg.NoCumulativeStats
- return pc, nil
- }
- // newPacketConn creates a net.PacketConn using the specified network
- // interface, wrapped socket and big endian protocol number.
- //
- // It is the entry point for tests in this package.
- func newPacketConn(ifi *net.Interface, s socket, pbe uint16, filter []bpf.RawInstruction) (*packetConn, error) {
- pc := &packetConn{
- ifi: ifi,
- s: s,
- pbe: pbe,
- }
- if len(filter) > 0 {
- if err := pc.SetBPF(filter); err != nil {
- return nil, err
- }
- }
- // Bind the packet socket to the interface specified by ifi
- // packet(7):
- // Only the sll_protocol and the sll_ifindex address fields are used for
- // purposes of binding.
- // This overrides the protocol given to socket(AF_PACKET).
- err := s.Bind(&unix.SockaddrLinklayer{
- Protocol: pc.pbe,
- Ifindex: ifi.Index,
- })
- if err != nil {
- return nil, err
- }
- return pc, nil
- }
- // ReadFrom implements the net.PacketConn.ReadFrom method.
- func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {
- // Attempt to receive on socket
- n, addr, err := p.s.Recvfrom(b, 0)
- if err != nil {
- return n, nil, err
- }
- // Retrieve hardware address and other information from addr.
- sa, ok := addr.(*unix.SockaddrLinklayer)
- if !ok || sa.Halen < 6 {
- return n, nil, unix.EINVAL
- }
- // Use length specified to convert byte array into a hardware address slice.
- mac := make(net.HardwareAddr, sa.Halen)
- copy(mac, sa.Addr[:])
- // packet(7):
- // sll_hatype and sll_pkttype are set on received packets for your
- // information.
- // TODO(mdlayher): determine if similar fields exist and are useful on
- // non-Linux platforms
- return n, &Addr{
- HardwareAddr: mac,
- }, nil
- }
- // WriteTo implements the net.PacketConn.WriteTo method.
- func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {
- // Ensure correct Addr type.
- a, ok := addr.(*Addr)
- if !ok || a.HardwareAddr == nil || len(a.HardwareAddr) < 6 {
- return 0, unix.EINVAL
- }
- // Convert hardware address back to byte array form.
- var baddr [8]byte
- copy(baddr[:], a.HardwareAddr)
- // Send message on socket to the specified hardware address from addr
- // 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.
- // In this case, sll_family is taken care of automatically by unix.
- err := p.s.Sendto(b, 0, &unix.SockaddrLinklayer{
- Ifindex: p.ifi.Index,
- Halen: uint8(len(a.HardwareAddr)),
- Addr: baddr,
- Protocol: p.pbe,
- })
- return len(b), err
- }
- // Close closes the connection.
- func (p *packetConn) Close() error {
- return p.s.Close()
- }
- // LocalAddr returns the local network address.
- func (p *packetConn) LocalAddr() net.Addr {
- return &Addr{
- HardwareAddr: p.ifi.HardwareAddr,
- }
- }
- // SetDeadline implements the net.PacketConn.SetDeadline method.
- func (p *packetConn) SetDeadline(t time.Time) error {
- return p.s.SetDeadline(t)
- }
- // SetReadDeadline implements the net.PacketConn.SetReadDeadline method.
- func (p *packetConn) SetReadDeadline(t time.Time) error {
- return p.s.SetReadDeadline(t)
- }
- // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method.
- func (p *packetConn) SetWriteDeadline(t time.Time) error {
- return p.s.SetWriteDeadline(t)
- }
- // SetBPF attaches an assembled BPF program to a raw net.PacketConn.
- func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error {
- prog := unix.SockFprog{
- Len: uint16(len(filter)),
- Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])),
- }
- err := p.s.SetSockoptSockFprog(
- unix.SOL_SOCKET,
- unix.SO_ATTACH_FILTER,
- &prog,
- )
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
- }
- // 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(b bool) error {
- mreq := unix.PacketMreq{
- Ifindex: int32(p.ifi.Index),
- Type: unix.PACKET_MR_PROMISC,
- }
- membership := unix.PACKET_ADD_MEMBERSHIP
- if !b {
- membership = unix.PACKET_DROP_MEMBERSHIP
- }
- return p.s.SetSockoptPacketMreq(unix.SOL_PACKET, membership, &mreq)
- }
- // Stats retrieves statistics from the Conn.
- func (p *packetConn) Stats() (*Stats, error) {
- stats, err := p.s.GetSockoptTpacketStats(unix.SOL_PACKET, unix.PACKET_STATISTICS)
- if err != nil {
- return nil, err
- }
- return p.handleStats(stats), nil
- }
- // handleStats handles creation of Stats structures from raw packet socket stats.
- func (p *packetConn) handleStats(s *unix.TpacketStats) *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,
- }
- }
- // sysSocket is the default socket implementation. It makes use of
- // Linux-specific system calls to handle raw socket functionality.
- type sysSocket struct {
- f *os.File
- rc syscall.RawConn
- }
- func (s *sysSocket) SetDeadline(t time.Time) error {
- return s.f.SetDeadline(t)
- }
- func (s *sysSocket) SetReadDeadline(t time.Time) error {
- return s.f.SetReadDeadline(t)
- }
- func (s *sysSocket) SetWriteDeadline(t time.Time) error {
- return s.f.SetWriteDeadline(t)
- }
- func (s *sysSocket) Bind(sa unix.Sockaddr) error {
- var err error
- cerr := s.rc.Control(func(fd uintptr) {
- err = unix.Bind(int(fd), sa)
- })
- if err != nil {
- return err
- }
- return cerr
- }
- func (s *sysSocket) Close() error {
- return s.f.Close()
- }
- func (s *sysSocket) GetSockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
- var stats *unix.TpacketStats
- var err error
- cerr := s.rc.Control(func(fd uintptr) {
- s, errno := unix.GetsockoptTpacketStats(int(fd), level, name)
- stats = s
- if errno != nil {
- err = os.NewSyscallError("getsockopt", errno)
- }
- })
- if err != nil {
- return stats, err
- }
- return stats, cerr
- }
- func (s *sysSocket) Recvfrom(p []byte, flags int) (n int, addr unix.Sockaddr, err error) {
- cerr := s.rc.Read(func(fd uintptr) bool {
- n, addr, err = unix.Recvfrom(int(fd), p, flags)
- // When the socket is in non-blocking mode, we might see EAGAIN
- // and end up here. In that case, return false to let the
- // poller wait for readiness. See the source code for
- // internal/poll.FD.RawRead for more details.
- //
- // If the socket is in blocking mode, EAGAIN should never occur.
- return err != unix.EAGAIN
- })
- if err != nil {
- return n, addr, err
- }
- return n, addr, cerr
- }
- func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error {
- var err error
- cerr := s.rc.Write(func(fd uintptr) bool {
- err = unix.Sendto(int(fd), p, flags, to)
- // See comment in Recvfrom.
- return err != unix.EAGAIN
- })
- if err != nil {
- return err
- }
- return cerr
- }
- func (s *sysSocket) SetSockoptSockFprog(level, name int, fprog *unix.SockFprog) error {
- var err error
- cerr := s.rc.Control(func(fd uintptr) {
- errno := unix.SetsockoptSockFprog(int(fd), level, name, fprog)
- if errno != nil {
- err = os.NewSyscallError("setsockopt", errno)
- }
- })
- if err != nil {
- return err
- }
- return cerr
- }
- func (s *sysSocket) SetSockoptPacketMreq(level, name int, mreq *unix.PacketMreq) error {
- var err error
- cerr := s.rc.Control(func(fd uintptr) {
- errno := unix.SetsockoptPacketMreq(int(fd), level, name, mreq)
- if errno != nil {
- err = os.NewSyscallError("setsockopt", errno)
- }
- })
- if err != nil {
- return err
- }
- return cerr
- }
|