Files
cabletest/sock.go
T

143 lines
3.6 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"runtime"
"unsafe"
"golang.org/x/sys/unix"
)
type mmsghdr struct {
hdr unix.Msghdr
len uint32
_ [4]byte
}
func htons(v uint16) uint16 { return v<<8 | v>>8 }
func setBufForce(fd, forceOpt, opt, size int) error {
if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, forceOpt, size); err == nil {
return nil
}
return unix.SetsockoptInt(fd, unix.SOL_SOCKET, opt, size)
}
func sockBufSize(fd, opt int) int {
v, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, opt)
if err != nil {
return -1
}
return v
}
const (
sndbufBytes = 8 << 20
rcvbufBytes = 64 << 20
)
func openTxSocket(ifindex int) (int, error) {
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, 0)
if err != nil {
return -1, fmt.Errorf("socket: %w", err)
}
if err := unix.Bind(fd, &unix.SockaddrLinklayer{Ifindex: ifindex}); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("bind ifindex %d: %w", ifindex, err)
}
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_QDISC_BYPASS, 1); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("qdisc bypass: %w", err)
}
if err := setBufForce(fd, unix.SO_SNDBUFFORCE, unix.SO_SNDBUF, sndbufBytes); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("sndbuf: %w", err)
}
return fd, nil
}
func openRxSocket(ifindex, fanoutID, fanoutMode int) (int, error) {
proto := int(htons(etherType))
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, proto)
if err != nil {
return -1, fmt.Errorf("socket: %w", err)
}
if err := setBufForce(fd, unix.SO_RCVBUFFORCE, unix.SO_RCVBUF, rcvbufBytes); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("rcvbuf: %w", err)
}
if err := unix.Bind(fd, &unix.SockaddrLinklayer{
Protocol: htons(etherType),
Ifindex: ifindex,
}); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("bind ifindex %d: %w", ifindex, err)
}
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_IGNORE_OUTGOING, 1); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("ignore outgoing: %w", err)
}
tv := unix.Timeval{Sec: 0, Usec: 200000}
if err := unix.SetsockoptTimeval(fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("rcvtimeo: %w", err)
}
if fanoutMode >= 0 {
arg := (fanoutMode << 16) | (fanoutID & 0xffff)
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, arg); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("fanout: %w", err)
}
}
return fd, nil
}
func sendmmsg(fd int, hdrs []mmsghdr) (int, error) {
n, _, errno := unix.Syscall6(unix.SYS_SENDMMSG, uintptr(fd),
uintptr(unsafe.Pointer(&hdrs[0])), uintptr(len(hdrs)), 0, 0, 0)
if errno != 0 {
return int(n), errno
}
return int(n), nil
}
func recvmmsg(fd int, hdrs []mmsghdr, flags int) (int, error) {
n, _, errno := unix.Syscall6(unix.SYS_RECVMMSG, uintptr(fd),
uintptr(unsafe.Pointer(&hdrs[0])), uintptr(len(hdrs)), uintptr(flags), 0, 0)
if errno != 0 {
return int(n), errno
}
return int(n), nil
}
func newMmsghdrs(bufs [][]byte) ([]mmsghdr, []unix.Iovec) {
hdrs := make([]mmsghdr, len(bufs))
iovs := make([]unix.Iovec, len(bufs))
for i := range bufs {
iovs[i].Base = &bufs[i][0]
iovs[i].Len = uint64(len(bufs[i]))
hdrs[i].hdr.Iov = &iovs[i]
hdrs[i].hdr.Iovlen = 1
}
return hdrs, iovs
}
func pinTo(cpu int) error {
if cpu < 0 {
return nil
}
runtime.LockOSThread()
var set unix.CPUSet
set.Zero()
set.Set(cpu)
return unix.SchedSetaffinity(0, &set)
}
func packetDrops(fd int) uint64 {
st, err := unix.GetsockoptTpacketStats(fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
if err != nil {
return 0
}
return uint64(st.Drops)
}