Skip to content

Commit

Permalink
experimental CGO- less afpacket (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
mosajjal committed Jan 20, 2023
1 parent 97339b3 commit 1107518
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 69 deletions.
98 changes: 54 additions & 44 deletions afpacket/afpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,6 @@ import (
"github.com/gopacket/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,35 +56,59 @@ type Stats struct {
Polls int64
}

type TpacketReqv2 struct {
blockSize uint32
blockNr uint32
frameSize uint32
frameNr uint32
}

type TpacketReqv3 struct {
blockSize uint32
blockNr uint32
frameSize uint32
frameNr uint32
retireBlkTov uint32
sizeOfPriv uint32
featureReqWord uint32
}

// SocketStats is a struct where socket stats are stored
type SocketStats C.struct_tpacket_stats
type SocketStats struct {
packets uint32
drops uint32
}

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

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

// SocketStatsV3 is a struct where socket stats for TPacketV3 are stored
type SocketStatsV3 C.struct_tpacket_stats_v3
type SocketStatsV3 struct {
packets uint32
drops uint32
freezeQCount uint32
}

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

// Drops returns the number of packets dropped on this socket.
func (s *SocketStatsV3) Drops() uint {
return uint(s.tp_drops)
return uint(s.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.freezeQCount)
}

// TPacket implements packet receiving for Linux AF_PACKET versions 1, 2, and 3.
Expand Down Expand Up @@ -181,21 +194,21 @@ 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)
var tp TpacketReqv2
tp.blockSize = uint32(h.opts.blockSize)
tp.blockNr = uint32(h.opts.numBlocks)
tp.frameSize = uint32(h.opts.frameSize)
tp.frameNr = uint32(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 {
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)
var tp TpacketReqv3
tp.blockSize = uint32(h.opts.blockSize)
tp.blockNr = uint32(h.opts.numBlocks)
tp.frameSize = uint32(h.opts.frameSize)
tp.frameNr = uint32(h.opts.framesPerBlock * h.opts.numBlocks)
tp.retireBlkTov = uint32(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)
}
Expand All @@ -204,6 +217,7 @@ func (h *TPacket) setUpRing() (err error) {
}
h.ring, err = unix.Mmap(h.fd, 0, totalSize, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
if err != nil {
fmt.Println("here")
return err
}
if h.ring == nil {
Expand Down Expand Up @@ -343,19 +357,17 @@ 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
slt := uint32(unsafe.Sizeof(ssv3))

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

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt)))
if err != nil {
Expand All @@ -372,31 +384,29 @@ func (h *TPacket) SocketStats() (SocketStats, SocketStatsV3, error) {
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
slt := uint32(unsafe.Sizeof(ssv3))

err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt)))
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.packets += ssv3.packets
h.socketStatsV3.drops += ssv3.drops
h.socketStatsV3.freezeQCount += ssv3.freezeQCount
return h.socketStats, h.socketStatsV3, nil
}
socklen := unsafe.Sizeof(h.socketStats)
slt := C.socklen_t(socklen)
var ss SocketStats
slt := uint32(unsafe.Sizeof(ss))

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

h.socketStats.tp_packets += ss.tp_packets
h.socketStats.tp_drops += ss.tp_drops
h.socketStats.packets += ss.packets
h.socketStats.drops += ss.drops
return h.socketStats, h.socketStatsV3, nil
}

Expand Down Expand Up @@ -512,8 +522,8 @@ 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)
arg := int(t) << 16
arg |= int(id)
return setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_FANOUT, unsafe.Pointer(&arg), unsafe.Sizeof(arg))
}

Expand Down
122 changes: 97 additions & 25 deletions afpacket/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ import (
"golang.org/x/sys/unix"
)

// #include <linux/if_packet.h>
// #include <linux/if_ether.h>
// #define VLAN_HLEN 4
import "C"

// 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
// tpacket1/tpacket2 packet header AND the tpacket3 block header. The big
Expand Down Expand Up @@ -61,8 +56,85 @@ 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 struct {
tp_status uint64
tp_len uint32
tp_snaplen uint32
tp_mac uint16
tp_net uint16
tp_sec uint32
tp_usec uint32
_ [4]byte
}
type tpacket_bd_ts struct {
ts_sec uint32
anon0 [4]byte
}

type tpacket_hdr_v1 struct {
block_status uint32
num_pkts uint32
offset_to_first_pkt uint32
blk_len uint32
seq_num uint64
ts_first_pkt tpacket_bd_ts
ts_last_pkt tpacket_bd_ts
}

type v2header struct {
tp_status uint32
tp_len uint32
tp_snaplen uint32
tp_mac uint16
tp_net uint16
tp_sec uint32
tp_nsec uint32
tp_vlan_tci uint16
tp_vlan_tpid uint16
tp_padding [4]byte
}

type sockAddrLL struct {
sll_family uint16
sll_protocol uint16
sll_ifindex int32
sll_hatype uint16
sll_pkttype uint8
sll_halen uint8
sll_addr [8]byte
}

type tpacketHdrVarient1 struct {
rxHash uint32
vlanTCI uint32
vlanTPID uint16
_ [2]byte
}

type tpacketBlockDesc struct {
version uint32
offsetToPriv uint32
hdr [40]byte
}

type tpacket3_hdr struct {
tp_next_offset uint32
tp_sec uint32
tp_nsec uint32
tp_snaplen uint32
tp_len uint32
tp_status uint32
tp_mac uint16
tp_net uint16
anon0 [12]byte
tp_padding [8]byte
}

const ETH_ALEN = 0x6
const VLAN_HLEN = 0x4
const sizeof_struct_tpacket2_hdr = 0x20
const sizeof_struct_tpacket3_hdr = 0x30
const sizeof_struct_tpacket_hdr = 0x20

func makeSlice(start uintptr, length int) (data []byte) {
slice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
Expand All @@ -76,10 +148,10 @@ 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)+VLAN_HLEN)
eth = append(eth, data[0:ETH_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[ETH_ALEN*2:]...)
}

func (h *v1header) getVLAN() int {
Expand All @@ -101,7 +173,7 @@ func (h *v1header) getLength() int {
return int(h.tp_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)))))
ll := (*sockAddrLL)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(sizeof_struct_tpacket_hdr)))))
return int(ll.sll_ifindex)
}
func (h *v1header) next() bool {
Expand All @@ -128,31 +200,31 @@ func (h *v2header) getLength() int {
return int(h.tp_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)))))
ll := (*sockAddrLL)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(sizeof_struct_tpacket2_hdr)))))
return int(ll.sll_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 *tpacketBlockDesc
blockhdr *tpacket_hdr_v1
packet *tpacket3_hdr
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 = (*tpacketBlockDesc)(block)
w.blockhdr = (*tpacket_hdr_v1)(unsafe.Pointer(&w.block.hdr[0]))
w.packet = (*tpacket3_hdr)(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)
hv1 := (*tpacketHdrVarient1)(unsafe.Pointer(&w.packet.anon0[0]))
return int(hv1.vlanTCI & 0xfff)
}
return -1
}
Expand All @@ -169,14 +241,14 @@ func (w *v3wrapper) getTime() time.Time {
func (w *v3wrapper) getData(opts *options) []byte {
data := makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(w.packet.tp_mac), int(w.packet.tp_snaplen))

hv1 := (*C.struct_tpacket_hdr_variant1)(unsafe.Pointer(&w.packet.anon0[0]))
return insertVlanHeader(data, int(hv1.tp_vlan_tci), opts)
hv1 := (*tpacketHdrVarient1)(unsafe.Pointer(&w.packet.anon0[0]))
return insertVlanHeader(data, int(hv1.vlanTCI), opts)
}
func (w *v3wrapper) getLength() int {
return int(w.packet.tp_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)))))
ll := (*sockAddrLL)(unsafe.Pointer(uintptr(unsafe.Pointer(w.packet)) + uintptr(tpAlign(int(sizeof_struct_tpacket3_hdr)))))
return int(ll.sll_ifindex)
}
func (w *v3wrapper) next() bool {
Expand All @@ -191,6 +263,6 @@ func (w *v3wrapper) next() bool {
} else {
next += uintptr(tpAlign(int(w.packet.tp_snaplen) + int(w.packet.tp_mac)))
}
w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(next))
w.packet = (*tpacket3_hdr)(unsafe.Pointer(next))
return true
}

0 comments on commit 1107518

Please sign in to comment.