Skip to content

Commit

Permalink
Merge pull request #1 from bi-zone/afpacket_nocgo
Browse files Browse the repository at this point in the history
Get rid of CGO in gopacket/afpacket
  • Loading branch information
yalegko authored Jul 9, 2021
2 parents 3eaba08 + 37f700d commit 2210b98
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 166 deletions.
108 changes: 48 additions & 60 deletions afpacket/afpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,6 @@ import (
"github.com/google/gopacket"
)

/*
#include <linux/if_packet.h> // AF_PACKET, sockaddr_ll
#include <linux/if_ether.h> // ETH_P_ALL
#include <sys/socket.h> // socket()
#include <unistd.h> // close()
#include <arpa/inet.h> // htons()
#include <sys/mman.h> // mmap(), munmap()
#include <poll.h> // poll()
*/
import "C"

var pageSize = unix.Getpagesize()

// ErrPoll returned by poll
Expand All @@ -67,34 +56,38 @@ type Stats struct {
}

// SocketStats is a struct where socket stats are stored
type SocketStats C.struct_tpacket_stats
type SocketStats struct {
unix.TpacketStats
}

// Packets returns the number of packets seen by this socket.
func (s *SocketStats) Packets() uint {
return uint(s.tp_packets)
return uint(s.TpacketStats.Packets)
}

// Drops returns the number of packets dropped on this socket.
func (s *SocketStats) Drops() uint {
return uint(s.tp_drops)
return uint(s.TpacketStats.Drops)
}

// SocketStatsV3 is a struct where socket stats for TPacketV3 are stored
type SocketStatsV3 C.struct_tpacket_stats_v3
type SocketStatsV3 struct {
unix.TpacketStatsV3
}

// Packets returns the number of packets seen by this socket.
func (s *SocketStatsV3) Packets() uint {
return uint(s.tp_packets)
return uint(s.TpacketStatsV3.Packets)
}

// Drops returns the number of packets dropped on this socket.
func (s *SocketStatsV3) Drops() uint {
return uint(s.tp_drops)
return uint(s.TpacketStatsV3.Drops)
}

// QueueFreezes returns the number of queue freezes on this socket.
func (s *SocketStatsV3) QueueFreezes() uint {
return uint(s.tp_freeze_q_cnt)
return uint(s.TpacketStatsV3.Freeze_q_cnt)
}

// TPacket implements packet receiving for Linux AF_PACKET versions 1, 2, and 3.
Expand Down Expand Up @@ -180,23 +173,25 @@ func (h *TPacket) setUpRing() (err error) {
totalSize := int(h.opts.framesPerBlock * h.opts.numBlocks * h.opts.frameSize)
switch h.tpVersion {
case TPacketVersion1, TPacketVersion2:
var tp C.struct_tpacket_req
tp.tp_block_size = C.uint(h.opts.blockSize)
tp.tp_block_nr = C.uint(h.opts.numBlocks)
tp.tp_frame_size = C.uint(h.opts.frameSize)
tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks)
if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil {
tp := unix.TpacketReq{
Block_size: uint32(h.opts.blockSize),
Block_nr: uint32(h.opts.numBlocks),
Frame_size: uint32(h.opts.frameSize),
Frame_nr: uint32(h.opts.framesPerBlock * h.opts.numBlocks),
}
if err := unix.SetsockoptTpacketReq(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, &tp); err != nil {
return fmt.Errorf("setsockopt packet_rx_ring: %v", err)
}
case TPacketVersion3:
var tp C.struct_tpacket_req3
tp.tp_block_size = C.uint(h.opts.blockSize)
tp.tp_block_nr = C.uint(h.opts.numBlocks)
tp.tp_frame_size = C.uint(h.opts.frameSize)
tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks)
tp.tp_retire_blk_tov = C.uint(h.opts.blockTimeout / time.Millisecond)
if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil {
return fmt.Errorf("setsockopt packet_rx_ring v3: %v", err)
tp := unix.TpacketReq3{
Block_size: uint32(h.opts.blockSize),
Block_nr: uint32(h.opts.numBlocks),
Frame_size: uint32(h.opts.frameSize),
Frame_nr: uint32(h.opts.framesPerBlock * h.opts.numBlocks),
Retire_blk_tov: uint32(h.opts.blockTimeout / time.Millisecond),
}
if err := unix.SetsockoptTpacketReq3(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, &tp); err != nil {
return fmt.Errorf("setsockopt packet_rx_ring: %v", err)
}
default:
return errors.New("invalid tpVersion")
Expand Down Expand Up @@ -270,7 +265,7 @@ func (h *TPacket) SetBPF(filter []bpf.RawInstruction) error {
p.Len = uint16(len(filter))
p.Filter = (*unix.SockFilter)(unsafe.Pointer(&filter[0]))

return setsockopt(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, unsafe.Pointer(&p), unix.SizeofSockFprog)
return unix.SetsockoptSockFprog(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &p)
}

func (h *TPacket) releaseCurrentPacket() error {
Expand Down Expand Up @@ -336,21 +331,13 @@ func (h *TPacket) Stats() (Stats, error) {
// InitSocketStats clears socket counters and return empty stats.
func (h *TPacket) InitSocketStats() error {
if h.tpVersion == TPacketVersion3 {
socklen := unsafe.Sizeof(h.socketStatsV3)
slt := C.socklen_t(socklen)
var ssv3 SocketStatsV3

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt)))
_, err := unix.GetsockoptTpacketStatsV3(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
if err != nil {
return err
}
h.socketStatsV3 = SocketStatsV3{}
} else {
socklen := unsafe.Sizeof(h.socketStats)
slt := C.socklen_t(socklen)
var ss SocketStats

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt)))
_, err := unix.GetsockoptTpacketStats(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
if err != nil {
return err
}
Expand All @@ -363,33 +350,27 @@ func (h *TPacket) InitSocketStats() error {
func (h *TPacket) SocketStats() (SocketStats, SocketStatsV3, error) {
h.statsMu.Lock()
defer h.statsMu.Unlock()

// We need to save the counters since asking for the stats will clear them
if h.tpVersion == TPacketVersion3 {
socklen := unsafe.Sizeof(h.socketStatsV3)
slt := C.socklen_t(socklen)
var ssv3 SocketStatsV3

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt)))
ssv3, err := unix.GetsockoptTpacketStatsV3(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
if err != nil {
return SocketStats{}, SocketStatsV3{}, err
}

h.socketStatsV3.tp_packets += ssv3.tp_packets
h.socketStatsV3.tp_drops += ssv3.tp_drops
h.socketStatsV3.tp_freeze_q_cnt += ssv3.tp_freeze_q_cnt
h.socketStatsV3.TpacketStatsV3.Packets += ssv3.Packets
h.socketStatsV3.TpacketStatsV3.Drops += ssv3.Drops
h.socketStatsV3.TpacketStatsV3.Freeze_q_cnt += ssv3.Freeze_q_cnt
return h.socketStats, h.socketStatsV3, nil
}
socklen := unsafe.Sizeof(h.socketStats)
slt := C.socklen_t(socklen)
var ss SocketStats

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt)))
ss, err := unix.GetsockoptTpacketStats(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
if err != nil {
return SocketStats{}, SocketStatsV3{}, err
}

h.socketStats.tp_packets += ss.tp_packets
h.socketStats.tp_drops += ss.tp_drops
h.socketStats.TpacketStats.Packets += ss.Packets
h.socketStats.TpacketStats.Drops += ss.Drops
return h.socketStats, h.socketStatsV3, nil
}

Expand Down Expand Up @@ -505,13 +486,20 @@ const (
func (h *TPacket) SetFanout(t FanoutType, id uint16) error {
h.mu.Lock()
defer h.mu.Unlock()
arg := C.int(t) << 16
arg |= C.int(id)
return setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_FANOUT, unsafe.Pointer(&arg), unsafe.Sizeof(arg))
arg := int(t) << 16
arg |= int(id)
return unix.SetsockoptInt(h.fd, unix.SOL_PACKET, unix.PACKET_FANOUT, arg)
}

// WritePacketData transmits a raw packet.
func (h *TPacket) WritePacketData(pkt []byte) error {
_, err := unix.Write(h.fd, pkt)
return err
}

// 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
}
96 changes: 48 additions & 48 deletions afpacket/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import (
"golang.org/x/sys/unix"
)

// #include <linux/if_packet.h>
// #include <linux/if_ether.h>
// #define VLAN_HLEN 4
import "C"
const (
// https://github.com/torvalds/linux/blob/master/include/linux/if_vlan.h#L16
cVLAN_HLEN = 4
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/if_ether.h#L32
cETH_ALEN = 6
)

// Our model of handling all TPacket versions is a little hacky, to say the
// least. We use the header interface to handle interactions with the
Expand Down Expand Up @@ -60,8 +62,8 @@ func tpAlign(x int) int {
return int((uint(x) + tpacketAlignment - 1) &^ (tpacketAlignment - 1))
}

type v1header C.struct_tpacket_hdr
type v2header C.struct_tpacket2_hdr
type v1header unix.TpacketHdr
type v2header unix.Tpacket2Hdr

func makeSlice(start uintptr, length int) (data []byte) {
slice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
Expand All @@ -75,33 +77,33 @@ func insertVlanHeader(data []byte, vlanTCI int, opts *options) []byte {
if vlanTCI == 0 || !opts.addVLANHeader {
return data
}
eth := make([]byte, 0, len(data)+C.VLAN_HLEN)
eth = append(eth, data[0:C.ETH_ALEN*2]...)
eth := make([]byte, 0, len(data)+cVLAN_HLEN)
eth = append(eth, data[0:cETH_ALEN*2]...)
eth = append(eth, []byte{0x81, 0, byte((vlanTCI >> 8) & 0xff), byte(vlanTCI & 0xff)}...)
return append(eth, data[C.ETH_ALEN*2:]...)
return append(eth, data[cETH_ALEN*2:]...)
}

func (h *v1header) getVLAN() int {
return -1
}
func (h *v1header) getStatus() int {
return int(h.tp_status)
return int(h.Status)
}
func (h *v1header) clearStatus() {
h.tp_status = 0
h.Status = 0
}
func (h *v1header) getTime() time.Time {
return time.Unix(int64(h.tp_sec), int64(h.tp_usec)*1000)
return time.Unix(int64(h.Sec), int64(h.Usec)*1000)
}
func (h *v1header) getData(opts *options) []byte {
return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen))
return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.Mac), int(h.Snaplen))
}
func (h *v1header) getLength() int {
return int(h.tp_len)
return int(h.Len)
}
func (h *v1header) getIfaceIndex() int {
ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket_hdr)))))
return int(ll.sll_ifindex)
ll := (*unix.RawSockaddrLinklayer)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(unix.SizeofTpacketHdr))))
return int(ll.Ifindex)
}
func (h *v1header) next() bool {
return false
Expand All @@ -111,85 +113,83 @@ func (h *v2header) getVLAN() int {
return -1
}
func (h *v2header) getStatus() int {
return int(h.tp_status)
return int(h.Status)
}
func (h *v2header) clearStatus() {
h.tp_status = 0
h.Status = 0
}
func (h *v2header) getTime() time.Time {
return time.Unix(int64(h.tp_sec), int64(h.tp_nsec))
return time.Unix(int64(h.Sec), int64(h.Nsec))
}
func (h *v2header) getData(opts *options) []byte {
data := makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen))
return insertVlanHeader(data, int(h.tp_vlan_tci), opts)
data := makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.Mac), int(h.Snaplen))
return insertVlanHeader(data, int(h.Vlan_tci), opts)
}
func (h *v2header) getLength() int {
return int(h.tp_len)
return int(h.Len)
}
func (h *v2header) getIfaceIndex() int {
ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket2_hdr)))))
return int(ll.sll_ifindex)
ll := (*unix.RawSockaddrLinklayer)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(unix.SizeofTpacket2Hdr))))
return int(ll.Ifindex)
}
func (h *v2header) next() bool {
return false
}

type v3wrapper struct {
block *C.struct_tpacket_block_desc
blockhdr *C.struct_tpacket_hdr_v1
packet *C.struct_tpacket3_hdr
used C.__u32
block *unix.TpacketBlockDesc
blockhdr *unix.TpacketHdrV1
packet *unix.Tpacket3Hdr
used uint32
}

func initV3Wrapper(block unsafe.Pointer) (w v3wrapper) {
w.block = (*C.struct_tpacket_block_desc)(block)
w.blockhdr = (*C.struct_tpacket_hdr_v1)(unsafe.Pointer(&w.block.hdr[0]))
w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(uintptr(block) + uintptr(w.blockhdr.offset_to_first_pkt)))
w.block = (*unix.TpacketBlockDesc)(block)
w.blockhdr = (*unix.TpacketHdrV1)(unsafe.Pointer(&w.block.Hdr[0]))
w.packet = (*unix.Tpacket3Hdr)(unsafe.Pointer(uintptr(block) + uintptr(w.blockhdr.Offset_to_first_pkt)))
return
}

func (w *v3wrapper) getVLAN() int {
if w.packet.tp_status&unix.TP_STATUS_VLAN_VALID != 0 {
hv1 := (*C.struct_tpacket_hdr_variant1)(unsafe.Pointer(&w.packet.anon0[0]))
return int(hv1.tp_vlan_tci & 0xfff)
if w.packet.Status&unix.TP_STATUS_VLAN_VALID != 0 {
return int(w.packet.Hv1.Vlan_tci & 0xfff)
}
return -1
}

func (w *v3wrapper) getStatus() int {
return int(w.blockhdr.block_status)
return int(w.blockhdr.Block_status)
}
func (w *v3wrapper) clearStatus() {
w.blockhdr.block_status = 0
w.blockhdr.Block_status = 0
}
func (w *v3wrapper) getTime() time.Time {
return time.Unix(int64(w.packet.tp_sec), int64(w.packet.tp_nsec))
return time.Unix(int64(w.packet.Sec), int64(w.packet.Nsec))
}
func (w *v3wrapper) getData(opts *options) []byte {
data := makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(w.packet.tp_mac), int(w.packet.tp_snaplen))
data := makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(w.packet.Mac), int(w.packet.Snaplen))

hv1 := (*C.struct_tpacket_hdr_variant1)(unsafe.Pointer(&w.packet.anon0[0]))
return insertVlanHeader(data, int(hv1.tp_vlan_tci), opts)
return insertVlanHeader(data, int(w.packet.Hv1.Vlan_tci), opts)
}
func (w *v3wrapper) getLength() int {
return int(w.packet.tp_len)
return int(w.packet.Len)
}
func (w *v3wrapper) getIfaceIndex() int {
ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(w.packet)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket3_hdr)))))
return int(ll.sll_ifindex)
ll := (*unix.RawSockaddrLinklayer)(unsafe.Pointer(uintptr(unsafe.Pointer(w.packet)) + uintptr(tpAlign(unix.SizeofTpacket3Hdr))))
return int(ll.Ifindex)
}
func (w *v3wrapper) next() bool {
w.used++
if w.used >= w.blockhdr.num_pkts {
if w.used >= w.blockhdr.Num_pkts {
return false
}

next := uintptr(unsafe.Pointer(w.packet))
if w.packet.tp_next_offset != 0 {
next += uintptr(w.packet.tp_next_offset)
if w.packet.Next_offset != 0 {
next += uintptr(w.packet.Next_offset)
} else {
next += uintptr(tpAlign(int(w.packet.tp_snaplen) + int(w.packet.tp_mac)))
next += uintptr(tpAlign(int(w.packet.Snaplen) + int(w.packet.Mac)))
}
w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(next))
w.packet = (*unix.Tpacket3Hdr)(unsafe.Pointer(next))
return true
}
Loading

0 comments on commit 2210b98

Please sign in to comment.