77 changes: 67 additions & 10 deletions afpacket/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type header interface {
getTime() time.Time
// getData returns the packet data pointed to by the current header.
getData(opts *options) []byte
// putData puts the packet data to the current header.
putData(data []byte)
// getLength returns the total length of the packet.
getLength() int
// getIfaceIndex returns the index of the network interface
Expand Down Expand Up @@ -96,6 +98,12 @@ func (h *v1header) getTime() time.Time {
func (h *v1header) getData(opts *options) []byte {
return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen))
}
func (h *v1header) putData(data []byte) {
d := makeSlice(uintptr(unsafe.Pointer(h))+uintptr(tpAlign(int(C.sizeof_struct_tpacket_hdr))), len(data))
copy(d, data)
h.tp_len = C.uint(len(data))
h.tp_status = unix.TP_STATUS_SEND_REQUEST
}
func (h *v1header) getLength() int {
return int(h.tp_len)
}
Expand Down Expand Up @@ -123,6 +131,12 @@ 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)
}
func (h *v2header) putData(data []byte) {
d := makeSlice(uintptr(unsafe.Pointer(h))+uintptr(tpAlign(int(C.sizeof_struct_tpacket2_hdr))), len(data))
copy(d, data)
h.tp_len = C.uint(len(data))
h.tp_status = unix.TP_STATUS_SEND_REQUEST
}
func (h *v2header) getLength() int {
return int(h.tp_len)
}
Expand All @@ -139,16 +153,31 @@ type v3wrapper struct {
blockhdr *C.struct_tpacket_hdr_v1
packet *C.struct_tpacket3_hdr
used C.__u32
rx bool
}

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)))
func initV3Wrapper(block unsafe.Pointer, rx bool) (w v3wrapper) {
if rx {
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.rx = true
} else {
// no block descriptor for tx
w.block = nil
w.blockhdr = nil
w.packet = (*C.struct_tpacket3_hdr)(block)
w.rx = false
}

return
}

func (w *v3wrapper) getVLAN() int {
if !w.rx {
return -1
}

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)
Expand All @@ -157,28 +186,56 @@ func (w *v3wrapper) getVLAN() int {
}

func (w *v3wrapper) getStatus() int {
return int(w.blockhdr.block_status)
if w.rx {
return int(w.blockhdr.block_status)
} else {
return int(w.packet.tp_status)
}
}
func (w *v3wrapper) clearStatus() {
w.blockhdr.block_status = 0
if w.rx {
w.blockhdr.block_status = 0
} else {
w.packet.tp_status = 0
}
}
func (w *v3wrapper) getTime() time.Time {
return time.Unix(int64(w.packet.tp_sec), int64(w.packet.tp_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))

hv1 := (*C.struct_tpacket_hdr_variant1)(unsafe.Pointer(&w.packet.anon0[0]))
return insertVlanHeader(data, int(hv1.tp_vlan_tci), opts)
if w.rx {
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)
} else {
return makeSlice(
uintptr(unsafe.Pointer(w.packet))+uintptr(tpAlign(int(C.sizeof_struct_tpacket3_hdr))),
int(w.packet.tp_len))
}
}
func (w *v3wrapper) putData(data []byte) {
d := makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(tpAlign(int(C.sizeof_struct_tpacket3_hdr))), len(data))
copy(d, data)
w.packet.tp_len = C.uint(len(data))
w.packet.tp_next_offset = 0
w.packet.tp_status = unix.TP_STATUS_SEND_REQUEST
}
func (w *v3wrapper) getLength() int {
return int(w.packet.tp_len)
}
func (w *v3wrapper) getIfaceIndex() int {
if !w.rx {
return -1
}

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)
}
func (w *v3wrapper) next() bool {
if !w.rx {
return false
}

w.used++
if w.used >= w.blockhdr.num_pkts {
return false
Expand Down
78 changes: 52 additions & 26 deletions afpacket/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ type OptBlockSize int
// It can be passed into NewTPacket.
type OptNumBlocks int

// RxRingOptions is the collection of options for TPacket's rx ring
// It can be passed into NewTPacket.
type RxRingOptions RingOptions

// TxRingOptions is the collection of options for TPacket's tx ring
// It can be passed into NewTPacket.
type TxRingOptions RingOptions

// OptBlockTimeout is TPacket v3's tp_retire_blk_tov. Note that it has only millisecond granularity, so must be >= 1 ms.
// It can be passed into NewTPacket.
type OptBlockTimeout time.Duration
Expand Down Expand Up @@ -115,23 +123,37 @@ const (
DefaultPollTimeout = -1 * time.Millisecond // Default value for OptPollTimeout. This blocks forever.
)

type options struct {
frameSize int
type RingOptions struct {
FrameSize int
BlockSize int
NumBlocks int
framesPerBlock int
blockSize int
numBlocks int
addVLANHeader bool
blockTimeout time.Duration
pollTimeout time.Duration
version OptTPacketVersion
socktype OptSocketType
iface string
}

type options struct {
rxRing RingOptions
txRing RingOptions
addVLANHeader bool
blockTimeout time.Duration
pollTimeout time.Duration
version OptTPacketVersion
socktype OptSocketType
iface string
}

var defaultOpts = options{
frameSize: DefaultFrameSize,
blockSize: DefaultBlockSize,
numBlocks: DefaultNumBlocks,
rxRing: RingOptions{
FrameSize: DefaultFrameSize,
BlockSize: DefaultBlockSize,
NumBlocks: DefaultNumBlocks,
framesPerBlock: DefaultBlockSize / DefaultFrameSize,
},
txRing: RingOptions{
FrameSize: DefaultFrameSize,
BlockSize: DefaultBlockSize,
NumBlocks: DefaultNumBlocks,
framesPerBlock: DefaultBlockSize / DefaultFrameSize,
},
blockTimeout: DefaultBlockTimeout,
pollTimeout: DefaultPollTimeout,
version: TPacketVersionHighestAvailable,
Expand All @@ -142,12 +164,12 @@ func parseOptions(opts ...interface{}) (ret options, err error) {
ret = defaultOpts
for _, opt := range opts {
switch v := opt.(type) {
case OptFrameSize:
ret.frameSize = int(v)
case OptBlockSize:
ret.blockSize = int(v)
case OptNumBlocks:
ret.numBlocks = int(v)
case RxRingOptions:
ret.rxRing = RingOptions(v)
ret.rxRing.framesPerBlock = v.BlockSize / v.FrameSize
case TxRingOptions:
ret.txRing = RingOptions(v)
ret.txRing.framesPerBlock = v.BlockSize / v.FrameSize
case OptBlockTimeout:
ret.blockTimeout = time.Duration(v)
case OptPollTimeout:
Expand All @@ -168,17 +190,21 @@ func parseOptions(opts ...interface{}) (ret options, err error) {
if err = ret.check(); err != nil {
return
}
ret.framesPerBlock = ret.blockSize / ret.frameSize

return
}
func (o options) check() error {
switch {
case o.blockSize%pageSize != 0:
return fmt.Errorf("block size %d must be divisible by page size %d", o.blockSize, pageSize)
case o.blockSize%o.frameSize != 0:
return fmt.Errorf("block size %d must be divisible by frame size %d", o.blockSize, o.frameSize)
case o.numBlocks < 1:
return fmt.Errorf("num blocks %d must be >= 1", o.numBlocks)
case o.rxRing.BlockSize%pageSize != 0:
return fmt.Errorf("rx ring: block size %d must be divisible by page size %d", o.rxRing.BlockSize, pageSize)
case o.rxRing.BlockSize%o.rxRing.FrameSize != 0:
return fmt.Errorf("rx ring: block size %d must be divisible by frame size %d", o.rxRing.BlockSize, o.rxRing.FrameSize)
case o.txRing.BlockSize%pageSize != 0:
return fmt.Errorf("tx ring: block size %d must be divisible by page size %d", o.txRing.BlockSize, pageSize)
case o.txRing.BlockSize%o.txRing.FrameSize != 0:
return fmt.Errorf("tx ring: block size %d must be divisible by frame size %d", o.txRing.BlockSize, o.txRing.FrameSize)
case o.rxRing.NumBlocks < 1 && o.txRing.NumBlocks < 1:
return fmt.Errorf("num blocks of both rx and tx rings cannot be < 1 at the same time")
case o.blockTimeout < time.Millisecond:
return fmt.Errorf("block timeout %v must be > %v", o.blockTimeout, time.Millisecond)
case o.version < tpacketVersionMin || o.version > tpacketVersionMax:
Expand Down
26 changes: 20 additions & 6 deletions examples/afpacket/afpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,33 @@ func newAfpacketHandle(device string, snaplen int, block_size int, num_blocks in

if device == "any" {
h.TPacket, err = afpacket.NewTPacket(
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.RxRingOptions{
FrameSize: snaplen,
BlockSize: block_size,
NumBlocks: num_blocks,
},
afpacket.TxRingOptions{
FrameSize: snaplen,
BlockSize: block_size,
NumBlocks: num_blocks,
},
afpacket.OptAddVLANHeader(useVLAN),
afpacket.OptPollTimeout(timeout),
afpacket.SocketRaw,
afpacket.TPacketVersion3)
} else {
h.TPacket, err = afpacket.NewTPacket(
afpacket.OptInterface(device),
afpacket.OptFrameSize(snaplen),
afpacket.OptBlockSize(block_size),
afpacket.OptNumBlocks(num_blocks),
afpacket.RxRingOptions{
FrameSize: snaplen,
BlockSize: block_size,
NumBlocks: num_blocks,
},
afpacket.TxRingOptions{
FrameSize: snaplen,
BlockSize: block_size,
NumBlocks: num_blocks,
},
afpacket.OptAddVLANHeader(useVLAN),
afpacket.OptPollTimeout(timeout),
afpacket.SocketRaw,
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.12
require (
github.com/vishvananda/netlink v1.1.0
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/sys v0.0.0-20200217220822-9197077df867
)
14 changes: 0 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,10 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=