diff --git a/afpacket/afpacket.go b/afpacket/afpacket.go index bdf888840..df1c08d8e 100644 --- a/afpacket/afpacket.go +++ b/afpacket/afpacket.go @@ -30,17 +30,6 @@ import ( "github.com/google/gopacket" ) -/* -#include // AF_PACKET, sockaddr_ll -#include // ETH_P_ALL -#include // socket() -#include // close() -#include // htons() -#include // mmap(), munmap() -#include // poll() -*/ -import "C" - var pageSize = unix.Getpagesize() // ErrPoll returned by poll @@ -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. @@ -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") @@ -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 { @@ -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 } @@ -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 } @@ -505,9 +486,9 @@ 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. @@ -515,3 +496,10 @@ 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 +} diff --git a/afpacket/header.go b/afpacket/header.go index 61634e7ab..226d6a6ba 100644 --- a/afpacket/header.go +++ b/afpacket/header.go @@ -16,10 +16,12 @@ import ( "golang.org/x/sys/unix" ) -// #include -// #include -// #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 @@ -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)) @@ -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 @@ -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 } diff --git a/afpacket/sockopt_linux.go b/afpacket/sockopt_linux.go deleted file mode 100644 index c53e1cceb..000000000 --- a/afpacket/sockopt_linux.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2012 Google, Inc. All rights reserved. -// -// Use of this source code is governed by a BSD-style license -// that can be found in the LICENSE file in the root of the source -// tree. - -// +build linux - -package afpacket - -import ( - "unsafe" - - "golang.org/x/sys/unix" -) - -// setsockopt provides access to the setsockopt syscall. -func setsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { - _, _, errno := unix.Syscall6( - unix.SYS_SETSOCKOPT, - uintptr(fd), - uintptr(level), - uintptr(name), - uintptr(val), - vallen, - 0, - ) - if errno != 0 { - return error(errno) - } - - return nil -} - -// getsockopt provides access to the getsockopt syscall. -func getsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { - _, _, errno := unix.Syscall6( - unix.SYS_GETSOCKOPT, - uintptr(fd), - uintptr(level), - uintptr(name), - uintptr(val), - vallen, - 0, - ) - if errno != 0 { - return error(errno) - } - - return nil -} - -// 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 -}