func (p *Pinger) read(ch chan<- sockMsg, numMsgs int) error {
	const mtu = 1500
	oobSize := unix.CmsgSpace(int(unsafe.Sizeof(unix.ScmTimestamping{}))) +
		unix.CmsgSpace(int(TTL(0).Len()))
	buffs, oobs, hs := makeMmsghdrs(numMsgs, mtu, oobSize)
	errQueueBuffs, errQueueOOBs, errQueueHs := makeMmsghdrs(numMsgs,
		mtu, oobSize+unix.CmsgSpace(
			int(unsafe.Sizeof(unix.SockExtendedErr{})+unix.SizeofSockaddrAny)))
	sendAndReset := func(hs []unixx.Mmsghdr, buffs [][]byte, oobs [][]byte) {
		for i := range hs {
			ch <- sockMsg{
				buff: append([]byte(nil), buffs[i][:hs[i].Len]...),
				oob:  append([]byte(nil), oobs[i][:hs[i].Hdr.Controllen]...),
			}
			hs[i].Hdr.SetControllen(len(oobs[i]))
		}
	}
	var (
		n, errQueueN         int
		operr, errQueueOperr error
	)
	for {
		if err := p.rc.Read(func(s uintptr) (done bool) {
			n, operr = unixx.Recvmmsg(s, hs, unix.MSG_DONTWAIT)
			errQueueN, errQueueOperr = unixx.Recvmmsg(s, errQueueHs, unix.MSG_ERRQUEUE|unix.MSG_DONTWAIT)
			return !(errIsWouldBlockOr(operr) && errIsWouldBlockOr(errQueueOperr))
		}); err != nil {
			return err
		}
		if errQueueOperr == nil {
			sendAndReset(errQueueHs[:errQueueN],
				errQueueBuffs[:errQueueN], errQueueOOBs[:errQueueN])
		} else if !errIsWouldBlockOr(errQueueOperr) {
			return os.NewSyscallError("recvmmsg", errQueueOperr)
		}
		if operr == nil {
			sendAndReset(hs[:n], buffs[:n], oobs[:n])
		} else if !errIsWouldBlockOr(operr, unix.EHOSTUNREACH) {
			return os.NewSyscallError("recvmmsg", operr)
		}
	}
}
read reads messgaes from underlying socket and its error queue
and sends them to channel.
numMsgs is the number of mmsghdrs to create.
package ping
import (
	"errors"
	"fmt"
	"time"
	"unsafe"
	"net"
	"os"
	unixx "github.com/mitinarseny/pingo/unix"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"golang.org/x/sys/unix"
)
func newConn(family, proto int, laddr *net.UDPAddr) (conn net.PacketConn, err error) {
	s, err := unix.Socket(family, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, proto)
	if err != nil {
		return nil, os.NewSyscallError("socket", err)
	}
	if err := unix.Bind(s, sockaddr(laddr)); err != nil {
		unix.Close(s)
		return nil, os.NewSyscallError("bind", err)
	}
	f := os.NewFile(uintptr(s), "datagram-oriented icmp")
	c, err := net.FilePacketConn(f)
	if cerr := f.Close(); cerr != nil {
		return nil, cerr
	}
	return c, err
}
func (p *Pinger) rLock() error {
	return p.c.SetReadDeadline(time.Now())
}
func (p *Pinger) rUnlock() error {
	return p.c.SetReadDeadline(time.Time{})
}
func (p *Pinger) sendSeq(seq uint16, dst net.IP, payload []byte, opts ...WOption) error {
	var typ icmp.Type
	switch p.proto {
	case unix.IPPROTO_ICMP:
		typ = ipv4.ICMPTypeEcho
	case unix.IPPROTO_ICMPV6:
		typ = ipv6.ICMPTypeEchoRequest
	}
	b, err := (&icmp.Message{
		Type: typ,
		Body: &icmp.Echo{
			Seq:  int(seq),
			Data: payload,
		},
	}).Marshal(nil)
	if err != nil {
		return fmt.Errorf("marshal ICMP: %w", err)
	}
	oob := marshalOpts(opts...)
	sa := sockaddr(&net.UDPAddr{IP: dst})
	var operr error
	if err := p.rc.Write(func(s uintptr) (done bool) {
		operr = unix.Sendmsg(int(s), b, oob, sa, 0)
		if operr == nil {
			p.optIDs.now(seq)
		}
		return !errIsWouldBlockOr(operr)
	}); err != nil {
		return err
	}
	return os.NewSyscallError("sendmsg", operr)
}
type sockMsg struct {
	buff, oob []byte
}