├── const.go ├── converters.go ├── examples ├── piperx │ └── main.go ├── sniffer │ └── main.go └── transparent │ ├── main.go │ └── netpacket │ ├── arp_header.go │ ├── byteorder.go │ ├── ether_header.go │ ├── headers.go │ ├── icmp_header.go │ ├── ip_header.go │ ├── ipv6_header.go │ ├── tcp_header.go │ └── udp_header.go ├── gen-converters.go ├── interface.go ├── internal.go ├── ioctl.go ├── netmap.go ├── request.go ├── ring.go └── utils.go /const.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | const netmapDev = "/dev/netmap" 4 | 5 | const ( 6 | NetmapOpenNoMmap = 0x040000 /* reuse mmap from parent */ 7 | NetmapOpenIfname = 0x080000 /* nr_name, nr_ringid, nr_flags */ 8 | NetmapOpenArg1 = 0x100000 9 | NetmapOpenArg2 = 0x200000 10 | NetmapOpenArg3 = 0x400000 11 | NetmapOpenRingCfg = 0x800000 /* tx|rx rings|slots */ 12 | ) 13 | const NR_REG_MASK = 0xf 14 | const RingTimestamp = 0x0002 /* set timestamp on *sync() */ 15 | const RingForward = 0x0004 /* enable NS_FORWARD for ring */ 16 | 17 | const NetmapHwRing = 0x4000 /* single NIC ring pair */ 18 | const NetmapSwRing = 0x2000 /* only host ring pair */ 19 | const NetmapRingMask = 0x0fff /* the ring number */ 20 | const NetmapNoTxPoll = 0x1000 /* no automatic txsync on poll */ 21 | const NetmapDoRxPoll = 0x8000 /* DO automatic rxsync on poll */ 22 | 23 | const NetmapRingMonitorTx = 0x100 24 | const NetmapRingMonitorRx = 0x200 25 | const NetmapRingZcopyMon = 0x400 26 | const NetmapRingExclusive = 0x800 /* request exclusive access to the selected rings */ 27 | const NetmapRingPtNetmapHost = 0x1000 /* request ptnetmap host support */ 28 | const NetmapRingRxRingsOnly = 0x2000 29 | const NetmapRingTxRingsOnly = 0x4000 30 | const NetmapRingAcceptVnetHdr = 0x8000 31 | 32 | const ( 33 | NetmapBdgAttach = 1 /* attach the NIC */ 34 | NetmapBdgDetach = 2 /* detach the NIC */ 35 | NetmapBdgRegops = 3 /* register bridge callbacks */ 36 | NetmapBdgList = 4 /* get bridge's info */ 37 | NetmapBdgVnetHdr = 5 /* set the port virtio-net-hdr length */ 38 | NetmapBdgNewIf = 6 /* create a virtual port */ 39 | NetmapBdgDelIf = 7 /* destroy a virtual port */ 40 | NetmapPtHostCreate = 8 /* create ptnetmap kthreads */ 41 | NetmapPtHostDelete = 9 /* delete ptnetmap kthreads */ 42 | NetmapBdgPollingOn = 10 /* delete polling kthread */ 43 | NetmapBdgPollingOff = 11 /* delete polling kthread */ 44 | NetmapVnetHdrGet = 12 /* get the port virtio-net-hdr length */ 45 | NetmapPoolsInfoGet = 13 /* get memory allocator pools info */ 46 | ) 47 | 48 | const NetmapBdgHost = 1 /* attach the host stack on ATTACH */ 49 | 50 | type Register int 51 | 52 | const ( 53 | ReqDefault Register = iota /* backward compat, should not be used. */ 54 | ReqAllNic = iota /* NR_REG_ALL_NIC, (default) all hardware ring pairs */ 55 | ReqSoftware = iota /* NR_REG_SW, the ``host rings'', connecting to the host stack. */ 56 | ReqNicSoftware = iota /* NR_REG_NIC_SW, all hardware rings and the host rings */ 57 | ReqOneNic = iota /* NR_REG_ONE_NIC, only the i-th hardware ring pair, where the number is in nr_ringid*/ 58 | ReqPipeMaster = iota /* NR_REG_PIPE_MASTER */ 59 | ReqPipeSlave = iota /* NR_REG_PIPE_SLAVE */ 60 | ) 61 | 62 | type Direction int 63 | 64 | const ( 65 | RX Direction = iota 66 | TX 67 | ) 68 | -------------------------------------------------------------------------------- /converters.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import "github.com/cheekybits/genny/generic" 4 | 5 | //go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "DstType=int,uint16,uint32" 6 | 7 | type DstType generic.Type 8 | 9 | func ifaceTODstType(i interface{}) DstType { 10 | var idx DstType 11 | switch i := i.(type) { 12 | case int: 13 | idx = DstType(i) 14 | case int16: 15 | idx = DstType(i) 16 | case int32: 17 | idx = DstType(i) 18 | case int64: 19 | idx = DstType(i) 20 | 21 | case uint: 22 | idx = DstType(i) 23 | case uint16: 24 | idx = DstType(i) 25 | case uint32: 26 | idx = DstType(i) 27 | case uint64: 28 | idx = DstType(i) 29 | } 30 | return idx 31 | } 32 | -------------------------------------------------------------------------------- /examples/piperx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pragus/gonetmap" 5 | "golang.org/x/sys/unix" 6 | ) 7 | 8 | type EtherHdr struct { 9 | DAddr [6]uint8 10 | SAddr [6]uint8 11 | EtherType [2]uint8 12 | } 13 | 14 | func ProcessSlot(r *gonetmap.NetmapRing, s *gonetmap.Slot) { 15 | buf := r.SlotBuffer(s) 16 | eth := (*EtherHdr)(buf) 17 | eth.EtherType = [2]byte{0x81, 00} 18 | } 19 | 20 | func ProcessRing(r *gonetmap.NetmapRing) uint16 { 21 | var i uint16 22 | cur := r.Cur 23 | for i := 0; !r.RingIsEmpty(); i++ { 24 | SlotPtr := r.Slot(cur) 25 | ProcessSlot(r, SlotPtr) 26 | cur = r.Next(cur) 27 | } 28 | 29 | return i 30 | 31 | } 32 | 33 | func PollingWorker(nif *gonetmap.Interface, ring *gonetmap.NetmapRing, timeout int) { 34 | fd := int32(nif.File.Fd()) 35 | events := make([]unix.PollFd, 1) 36 | events[0] = unix.PollFd{Fd: fd, Events: unix.POLLIN, Revents: 0} 37 | for { 38 | unix.Poll(events, timeout) 39 | ProcessRing(ring) 40 | 41 | } 42 | } 43 | 44 | func main() { 45 | netmap := gonetmap.New() 46 | req0 := gonetmap.Request{Version: 11, RingId: 0, Flags: gonetmap.ReqPipeMaster, Arg1: 0} 47 | req0.SetName("p") 48 | 49 | iface0, _ := netmap.RegIf(&req0) 50 | rxq0 := iface0.GetRing(0, gonetmap.RX) 51 | PollingWorker(iface0, rxq0, 0) 52 | 53 | } 54 | -------------------------------------------------------------------------------- /examples/sniffer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/pragus/gonetmap" 7 | "golang.org/x/sys/unix" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type EtherHdr struct { 13 | DAddr [6]byte 14 | SAddr [6]byte 15 | EtherType [2]byte 16 | } 17 | 18 | type VlanHdr struct { 19 | VlanTCI [2]byte 20 | VlanProto [2]byte 21 | } 22 | 23 | type EtherVlanHdr struct { 24 | EtherHdr 25 | VlanHdr 26 | } 27 | 28 | const MACFmt = "%#04x, %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x " 29 | 30 | func ProcessSlot(r *gonetmap.NetmapRing, s *gonetmap.Slot) { 31 | buf := r.SlotBuffer(s) 32 | eth := (*EtherHdr)(buf) 33 | 34 | fmt.Printf(MACFmt, eth.EtherType, 35 | eth.SAddr[0], eth.SAddr[1], eth.SAddr[2], eth.SAddr[3], eth.SAddr[4], eth.SAddr[5], 36 | eth.DAddr[0], eth.DAddr[1], eth.DAddr[2], eth.DAddr[3], eth.DAddr[4], eth.DAddr[5], 37 | ) 38 | 39 | switch eth.EtherType { 40 | case [2]byte{0x81, 00}: 41 | { 42 | eth := (*EtherVlanHdr)(buf) 43 | fmt.Printf("tci: %08b proto: %#04x\n", eth.VlanTCI, eth.VlanProto) 44 | } 45 | 46 | default: 47 | fmt.Printf("\n") 48 | 49 | } 50 | 51 | } 52 | 53 | func ProcessRing(r *gonetmap.NetmapRing) uint16 { 54 | var i uint16 55 | cur := r.Cur 56 | for i := 0; !r.RingIsEmpty(); i++ { 57 | SlotPtr := r.Slot(cur) 58 | ProcessSlot(r, SlotPtr) 59 | cur = r.Next(cur) 60 | } 61 | 62 | return i 63 | 64 | } 65 | 66 | func PollingWorker(nif *gonetmap.Interface, ring *gonetmap.NetmapRing, timeout int) { 67 | fd := int32(nif.File.Fd()) 68 | events := make([]unix.PollFd, 1, 1) 69 | events[0] = unix.PollFd{Fd: fd, Events: unix.POLLIN, Revents: 0} 70 | for { 71 | _, err := unix.Poll(events, timeout) 72 | if err == nil { 73 | ProcessRing(ring) 74 | } 75 | 76 | } 77 | } 78 | 79 | func main() { 80 | rawifaceData := flag.String("iface", "eth0:0", "interface:netmap_ring to listen") 81 | flag.Parse() 82 | 83 | ifaceData := strings.Split(*rawifaceData, ":") 84 | ringIndex, _ := strconv.Atoi(ifaceData[1]) 85 | 86 | netmap := gonetmap.New() 87 | req0 := gonetmap.Request{Version: 11, RingId: 0, Flags: gonetmap.ReqAllNic, Arg1: 0} 88 | req0.SetName(ifaceData[0]) 89 | 90 | iface0, _ := netmap.RegIf(&req0) 91 | rxq0 := iface0.GetRing(ringIndex, gonetmap.RX) 92 | 93 | PollingWorker(iface0, rxq0, 5) 94 | 95 | } 96 | -------------------------------------------------------------------------------- /examples/transparent/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | np "../transparent/netpacket" 5 | "flag" 6 | "fmt" 7 | "github.com/pragus/gonetmap" 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | const MACFmt = "%#04x, %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x, " 12 | 13 | func ProcessSlot(r *gonetmap.NetmapRing, s *gonetmap.Slot) { 14 | buf := r.SlotBuffer(s) 15 | eth := (*np.EtherHdr)(buf) 16 | s.Flags = gonetmap.RingForward 17 | 18 | 19 | fmt.Printf(MACFmt, eth.EtherType, 20 | eth.SAddr[0], eth.SAddr[1], eth.SAddr[2], eth.SAddr[3], eth.SAddr[4], eth.SAddr[5], 21 | eth.DAddr[0], eth.DAddr[1], eth.DAddr[2], eth.DAddr[3], eth.DAddr[4], eth.DAddr[5], 22 | ) 23 | 24 | switch eth.EtherType { 25 | case np.IPV4Number: 26 | { 27 | ip := eth.GetIP() 28 | ip.TimeToLive = 128 29 | ip.UpdateChecksum() 30 | fmt.Printf("%+v\n", *ip) 31 | } 32 | case np.ARPNumber: 33 | arp := eth.GetARP() 34 | fmt.Printf("%+v\n", *arp) 35 | default: 36 | fmt.Printf("\n") 37 | 38 | } 39 | 40 | } 41 | 42 | func ProcessRing(r *gonetmap.NetmapRing) uint16 { 43 | var i uint16 44 | r.Flags = gonetmap.RingForward 45 | cur := r.Cur 46 | for i := 0; !r.RingIsEmpty(); i++ { 47 | SlotPtr := r.Slot(cur) 48 | ProcessSlot(r, SlotPtr) 49 | cur = r.Next(cur) 50 | } 51 | 52 | return i 53 | 54 | } 55 | 56 | func PollingWorker(nif *gonetmap.Interface, timeout int) { 57 | fd := int32(nif.File.Fd()) 58 | events := make([]unix.PollFd, 1, 1) 59 | events[0] = unix.PollFd{Fd: fd, Events: unix.POLLIN, Revents: 0} 60 | 61 | for { 62 | _, err := unix.Poll(events, timeout) 63 | if err == nil { 64 | for ringIndex := uint32(0); ringIndex < nif.Nif.RxRings+1; ringIndex++ { 65 | ring := nif.GetRing(ringIndex, gonetmap.RX) 66 | ProcessRing(ring) 67 | 68 | } 69 | nif.RxSync() 70 | } 71 | 72 | } 73 | } 74 | 75 | func main() { 76 | rawifaceData := flag.String("iface", "eth0", "interface for transparent filtering") 77 | flag.Parse() 78 | 79 | netmap := gonetmap.New() 80 | req0 := gonetmap.Request{Version: 11, RingId: 0, Flags: gonetmap.ReqNicSoftware, Arg1: 0} 81 | req0.SetName(*rawifaceData) 82 | 83 | iface, _ := netmap.RegIf(&req0) 84 | PollingWorker(iface, 1) 85 | 86 | } 87 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/arp_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | type ARPHdr struct { 4 | HType [2]byte // Hardware type, e.g. 1 for Ethernet 5 | PType [2]byte // Protocol type, e.g. 0x0800 for IPv4 6 | HLen uint8 // Hardware address length, e.g. 6 for MAC length 7 | PLen uint8 // Protocol address length, e.g. 4 for IPv4 address length 8 | Operation uint16 // Operation type, see ARP constants 9 | SHA [EtherAddrLen]uint8 // Sender hardware address (sender MAC address) 10 | SPA [IPv4AddrLen]uint8 // Sender protocol address (sender IPv4 address) 11 | // array is used to avoid alignment (compiler aligns uint32 on 4 bytes) 12 | THA [EtherAddrLen]uint8 // Target hardware address (target MAC address) 13 | TPA [IPv4AddrLen]uint8 // Target protocol address (target IPv4 address) 14 | 15 | } 16 | 17 | // ARP protocol operations 18 | const ( 19 | ARPRequest = 1 20 | ARPReply = 2 21 | ) 22 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/byteorder.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | import ( 4 | "encoding/binary" 5 | "unsafe" 6 | ) 7 | 8 | func Ntohl(i uint32) uint32 { 9 | return binary.BigEndian.Uint32((*(*[4]byte)(unsafe.Pointer(&i)))[:]) 10 | } 11 | func Htonl(i uint32) uint32 { 12 | b := make([]byte, 4) 13 | binary.BigEndian.PutUint32(b, i) 14 | return *(*uint32)(unsafe.Pointer(&b[0])) 15 | } 16 | 17 | func Ntohs(i uint16) uint16 { 18 | return binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&i)))[:]) 19 | } 20 | func Htons(i uint16) uint16 { 21 | b := make([]byte, 2) 22 | binary.BigEndian.PutUint16(b, i) 23 | return *(*uint16)(unsafe.Pointer(&b[0])) 24 | } 25 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/ether_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | func (e *EtherHdr) String() string { 9 | return fmt.Sprintf("%#04x, %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x", 10 | e.EtherType, 11 | e.SAddr[0], e.SAddr[1], e.SAddr[2], e.SAddr[3], e.SAddr[4], e.SAddr[5], 12 | e.DAddr[0], e.DAddr[1], e.DAddr[2], e.DAddr[3], e.DAddr[4], e.DAddr[5], 13 | ) 14 | } 15 | 16 | func (e *EtherHdr) getNext() unsafe.Pointer { 17 | return unsafe.Pointer(uintptr(unsafe.Pointer(e)) + EtherLen) 18 | } 19 | 20 | func (e *EtherHdr) GetIP() *IPv4Hdr { 21 | return (*IPv4Hdr)(e.getNext()) 22 | } 23 | 24 | func (e *EtherHdr) GetARP() *ARPHdr { 25 | return (*ARPHdr)(e.getNext()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/headers.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | const ( 4 | EtherAddrLen = 6 5 | IPv4AddrLen = 4 6 | IPv6AddrLen = 16 7 | ) 8 | 9 | const ( 10 | EtherLen = 14 11 | VLANLen = 4 12 | MPLSLen = 4 13 | IPv4MinLen = 20 14 | IPv6Len = 40 15 | ICMPLen = 8 16 | TCPMinLen = 20 17 | UDPLen = 8 18 | ARPLen = 28 19 | ) 20 | 21 | // Supported EtherType for L2 22 | var ( 23 | IPV4Number = [2]byte{0x08, 0x00} 24 | ARPNumber = [2]byte{0x08, 0x06} 25 | VLANNumber = [2]byte{0x81, 0x00} 26 | MPLSNumber = [2]byte{0x88, 0x47} 27 | IPV6Number = [2]byte{0x86, 0xdd} 28 | ) 29 | 30 | // Supported L4 types 31 | const ( 32 | ICMPNumber = 0x01 33 | IPNumber = 0x04 34 | TCPNumber = 0x06 35 | UDPNumber = 0x11 36 | NoNextHeader = 0x3B 37 | ) 38 | 39 | // TCPFlags contains set TCP flags. 40 | type TCPFlags uint8 41 | 42 | // Constants for values of TCP flags. 43 | const ( 44 | TCPFlagFin = 0x01 45 | TCPFlagSyn = 0x02 46 | TCPFlagRst = 0x04 47 | TCPFlagPsh = 0x08 48 | TCPFlagAck = 0x10 49 | TCPFlagUrg = 0x20 50 | TCPFlagEce = 0x40 51 | TCPFlagCwr = 0x80 52 | ) 53 | 54 | // Supported ICMP Types 55 | const ( 56 | ICMPTypeEchoRequest uint8 = 8 57 | ICMPTypeEchoResponse uint8 = 0 58 | ) 59 | 60 | type EtherHdr struct { 61 | DAddr [EtherAddrLen]byte 62 | SAddr [EtherAddrLen]byte 63 | EtherType [2]byte 64 | } 65 | 66 | type VlanHdr struct { 67 | VlanTCI [2]byte 68 | VlanProto [2]byte 69 | } 70 | 71 | type EtherVlanHdr struct { 72 | EtherHdr 73 | VlanHdr 74 | } 75 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/icmp_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | type ICMPHdr struct { 4 | Type uint8 // ICMP message type 5 | Code uint8 // ICMP message code 6 | Cksum uint16 // ICMP checksum 7 | Identifier uint16 // ICMP message identifier in some messages 8 | SeqNum uint16 // ICMP message sequence number in some messages 9 | } 10 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/ip_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | import ( 4 | "encoding/binary" 5 | "unsafe" 6 | "github.com/pragus/gonetmap" 7 | 8 | ) 9 | 10 | const ( 11 | Idx0 = 0 12 | Idx1 = 2 13 | Idx2 = 4 14 | Idx3 = 6 15 | Idx4 = 8 16 | Idx5 = 12 17 | Idx6 = 14 18 | Idx7 = 16 19 | Idx8 = 18 20 | 21 | ) 22 | 23 | 24 | type IPv4Hdr struct { 25 | VersionIhl uint8 // version and header length 26 | TypeOfService uint8 // type of service 27 | TotalLength uint16 // length of packet 28 | PacketID uint16 // packet ID 29 | FragmentOffset uint16 // fragmentation offset 30 | TimeToLive uint8 // time to live 31 | NextProtoID uint8 // protocol ID 32 | HdrChecksum uint16 // header checksum 33 | SrcAddr [IPv4AddrLen]byte // source address 34 | DstAddr [IPv4AddrLen]byte // destination address 35 | } 36 | 37 | func (h *IPv4Hdr) UpdateChecksum() { 38 | 39 | b := *(*[]byte)(gonetmap.PtrSliceFrom(unsafe.Pointer(h), IPv4MinLen)) 40 | 41 | chk := uint32(binary.BigEndian.Uint16(b[Idx0: Idx0+2])) 42 | chk += uint32(binary.BigEndian.Uint16(b[Idx1: Idx1+2])) 43 | chk += uint32(binary.BigEndian.Uint16(b[Idx2: Idx2+2])) 44 | chk += uint32(binary.BigEndian.Uint16(b[Idx3: Idx3+2])) 45 | chk += uint32(binary.BigEndian.Uint16(b[Idx4: Idx4+2])) 46 | chk += uint32(binary.BigEndian.Uint16(b[Idx5: Idx5+2])) 47 | chk += uint32(binary.BigEndian.Uint16(b[Idx6: Idx6+2])) 48 | chk += uint32(binary.BigEndian.Uint16(b[Idx7: Idx7+2])) 49 | chk += uint32(binary.BigEndian.Uint16(b[Idx8: Idx8+2])) 50 | 51 | // "The first 4 bits are the carry and will be added to the rest of 52 | // the value." 53 | carry := uint16(chk >> 16) 54 | csum := ^(carry + uint16(chk & 0x0ffff)) 55 | h.HdrChecksum = Htons(csum) 56 | } 57 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/ipv6_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | type IPv6Hdr struct { 4 | VtcFlow [2]byte // IP version, traffic class & flow label 5 | PayloadLen [2]byte // IP packet length - includes sizeof(ip_header) 6 | Proto uint8 // Protocol, next header 7 | HopLimits uint8 // Hop limits 8 | SrcAddr [IPv6AddrLen]byte // IP address of source host 9 | DstAddr [IPv6AddrLen]byte // IP address of destination host(s) 10 | } 11 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/tcp_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | type TCPHdr struct { 4 | SrcPort uint16 // TCP source port 5 | DstPort uint16 // TCP destination port 6 | SentSeq uint32 // TX data sequence number 7 | RecvAck uint32 // RX data acknowledgement sequence number 8 | DataOff uint8 // Data offset 9 | TCPFlags TCPFlags // TCP flags 10 | RxWin uint16 // RX flow control window 11 | Cksum uint16 // TCP checksum 12 | TCPUrp uint16 // TCP urgent pointer, if any 13 | } 14 | -------------------------------------------------------------------------------- /examples/transparent/netpacket/udp_header.go: -------------------------------------------------------------------------------- 1 | package netpacket 2 | 3 | type UDPHdr struct { 4 | SrcPort uint16 // UDP source port 5 | DstPort uint16 // UDP destination port 6 | DgramLen uint16 // UDP datagram length 7 | DgramCksum uint16 // UDP datagram checksum 8 | } 9 | -------------------------------------------------------------------------------- /gen-converters.go: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by genny. 2 | // Any changes will be lost if this file is regenerated. 3 | // see https://github.com/cheekybits/genny 4 | 5 | package gonetmap 6 | 7 | func ifaceTOInt(i interface{}) int { 8 | var idx int 9 | switch i := i.(type) { 10 | case int: 11 | idx = int(i) 12 | case int16: 13 | idx = int(i) 14 | case int32: 15 | idx = int(i) 16 | case int64: 17 | idx = int(i) 18 | 19 | case uint: 20 | idx = int(i) 21 | case uint16: 22 | idx = int(i) 23 | case uint32: 24 | idx = int(i) 25 | case uint64: 26 | idx = int(i) 27 | } 28 | return idx 29 | } 30 | 31 | func ifaceTOUint16(i interface{}) uint16 { 32 | var idx uint16 33 | switch i := i.(type) { 34 | case int: 35 | idx = uint16(i) 36 | case int16: 37 | idx = uint16(i) 38 | case int32: 39 | idx = uint16(i) 40 | case int64: 41 | idx = uint16(i) 42 | 43 | case uint: 44 | idx = uint16(i) 45 | case uint16: 46 | idx = uint16(i) 47 | case uint32: 48 | idx = uint16(i) 49 | case uint64: 50 | idx = uint16(i) 51 | } 52 | return idx 53 | } 54 | 55 | func ifaceTOUint32(i interface{}) uint32 { 56 | var idx uint32 57 | switch i := i.(type) { 58 | case int: 59 | idx = uint32(i) 60 | case int16: 61 | idx = uint32(i) 62 | case int32: 63 | idx = uint32(i) 64 | case int64: 65 | idx = uint32(i) 66 | 67 | case uint: 68 | idx = uint32(i) 69 | case uint16: 70 | idx = uint32(i) 71 | case uint32: 72 | idx = uint32(i) 73 | case uint64: 74 | idx = uint32(i) 75 | } 76 | return idx 77 | } 78 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "os" 5 | "unsafe" 6 | ) 7 | 8 | type NetmapIf struct { 9 | Name [16]byte 10 | Version uint32 11 | Flags uint32 12 | TxRings uint32 13 | RxRings uint32 14 | BufsHead uint32 15 | Spare1 [5]uint32 16 | RingOffset unsafe.Pointer // NmRing is here 17 | } 18 | 19 | type Interface struct { 20 | File *os.File 21 | Nif *NetmapIf 22 | } 23 | 24 | func (i *Interface) ring(idx uint32) uintptr { 25 | ptr := unsafe.Pointer(uintptr(unsafe.Pointer(i.Nif)) + unsafe.Offsetof(i.Nif.RingOffset)) 26 | h := *(*[]uintptr)(PtrSliceFrom(ptr, int(i.Nif.TxRings+i.Nif.RxRings+2))) 27 | return uintptr(unsafe.Pointer(i.Nif)) + h[idx] 28 | 29 | } 30 | 31 | func (i *Interface) GetRing(ringIndex interface{}, direction Direction) *NetmapRing { 32 | var ringPtr uintptr 33 | idx := ifaceTOuint32(ringIndex) 34 | if direction == TX { 35 | ringPtr = i.ring(idx) 36 | } else { 37 | ringPtr = i.ring(idx + i.Nif.TxRings + 1) 38 | } 39 | return (*NetmapRing)(unsafe.Pointer(ringPtr)) 40 | } 41 | 42 | func (i *Interface) RxSync() error { 43 | return NmIoctl(i.File, NRxSync, unsafe.Pointer(uintptr(0))) 44 | } 45 | 46 | func (i *Interface) TxSync() error { 47 | return NmIoctl(i.File, NTxSync, unsafe.Pointer(uintptr(0))) 48 | } 49 | -------------------------------------------------------------------------------- /internal.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | type Stat struct { 8 | Received uint32 9 | Dropped uint32 10 | IfDropped uint32 11 | } 12 | 13 | type Slot struct { 14 | Idx uint32 15 | Len uint16 16 | Flags uint16 17 | Ptr uintptr 18 | } 19 | 20 | type PacketHeader struct { 21 | Ts syscall.Timeval 22 | Caplen uint32 23 | Len uint32 24 | Flags uint64 25 | Desc *Descriptor 26 | Slot *Slot 27 | Buf *uint8 28 | } 29 | 30 | type Descriptor struct { 31 | Self *Descriptor 32 | Fd int32 33 | pad0 [4]byte 34 | Mem *byte 35 | Memsize uint32 36 | DoneMmap int32 37 | NetmapIf *NetmapIf 38 | FirstTxRing uint16 39 | LastTxRing uint16 40 | CurTxRing uint16 41 | FirstRxRing uint16 42 | LastRxRing uint16 43 | CurRxRing uint16 44 | Request Request 45 | Header PacketHeader 46 | SomeRing *NetmapRing 47 | BufStart *byte 48 | BufEnd *byte 49 | Snaplen int32 50 | Promisc int32 51 | ToMs int32 52 | pad1 [4]byte 53 | ErrBuf *int8 54 | IfaceFlags uint32 55 | IfaceReqcap uint32 56 | IfaceCurcap uint32 57 | Stat Stat 58 | Msg [512]byte 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ioctl.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "github.com/paypal/gatt/linux/gioctl" 5 | "os" 6 | "unsafe" 7 | ) 8 | 9 | const IOINT = uintptr('i') 10 | const NmReqSize = unsafe.Sizeof(Request{}) 11 | 12 | var nInfo = gioctl.IoRW(IOINT, 145, NmReqSize) // _IOWR('i', 145, struct nmreq) 13 | var nRegIf = gioctl.IoRW(IOINT, 146, NmReqSize) // _IOWR('i', 146, struct nmreq) 14 | var NTxSync = gioctl.Io(IOINT, 148) // _IO('i', 148) /* sync tx queues */ 15 | var NRxSync = gioctl.Io(IOINT, 149) // _IO('i', 149) /* sync rx queues */ 16 | 17 | func NmIoctl(file *os.File, op uintptr, arg unsafe.Pointer) error { 18 | return gioctl.Ioctl(file.Fd(), op, uintptr(unsafe.Pointer(arg))) 19 | 20 | } 21 | 22 | func nmInfo(file *os.File, r *Request) error { 23 | return NmIoctl(file, nInfo, unsafe.Pointer(r)) 24 | } 25 | 26 | func nmRegIf(file *os.File, r *Request) error { 27 | return NmIoctl(file, nRegIf, unsafe.Pointer(r)) 28 | } 29 | -------------------------------------------------------------------------------- /netmap.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "unsafe" 8 | ) 9 | 10 | type Netmap struct { 11 | File *os.File 12 | MemReg uintptr 13 | lock sync.Mutex 14 | } 15 | 16 | func (n *Netmap) RegIf(r *Request) (i *Interface, err error) { 17 | file, _ := n.open() 18 | if err = nmRegIf(file, r); err == nil { 19 | fmt.Printf("ioctl: %v, fd: %v, %+v\n", err, file.Fd(), *r) 20 | if err = n.mmap(file, r); err == nil { 21 | nif := (*NetmapIf)(unsafe.Pointer(n.MemReg + uintptr(r.Offset))) 22 | return &Interface{Nif: nif, File: file}, err 23 | } 24 | } 25 | fmt.Printf("ioctl: %v, %+v\n", err, *r) 26 | return i, err 27 | } 28 | 29 | func (n *Netmap) mmap(file *os.File, r *Request) error { 30 | n.lock.Lock() 31 | defer n.lock.Unlock() 32 | 33 | if n.MemReg != 0 { 34 | return nil 35 | } 36 | 37 | if ptr, err := nmMmap(file, r); err == nil { 38 | n.MemReg = ptr 39 | return nil 40 | } else { 41 | return err 42 | } 43 | } 44 | 45 | func (n *Netmap) open() (*os.File, error) { 46 | file, err := os.OpenFile(netmapDev, os.O_RDWR, 0644) 47 | if err == nil { 48 | if n.File == nil { 49 | n.lock.Lock() 50 | n.File = file 51 | n.lock.Unlock() 52 | } 53 | } 54 | return file, err 55 | } 56 | 57 | func (n *Netmap) Info(r *Request) error { 58 | return nmInfo(n.File, r) 59 | } 60 | 61 | func New() *Netmap { 62 | return &Netmap{File: nil, MemReg: uintptr(0)} 63 | } 64 | -------------------------------------------------------------------------------- /request.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | type Request struct { 4 | Name [16]byte 5 | Version uint32 6 | Offset uint32 7 | Memsize uint32 8 | TxSlots uint32 9 | RxSlots uint32 10 | TxRings uint16 11 | RxRings uint16 12 | RingId uint16 13 | Cmd uint16 14 | Arg1 uint16 15 | Arg2 uint16 16 | Arg3 uint32 17 | Flags uint32 18 | Spare2 [1]uint32 19 | } 20 | 21 | func (r *Request) SetName(ifname string) { 22 | copy(r.Name[:], ifname) 23 | } 24 | 25 | func NewRequest() *Request { 26 | return &Request{Version: 11} 27 | } 28 | -------------------------------------------------------------------------------- /ring.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | type NetmapRing struct { 9 | BufOffset uintptr 10 | NumSlots uint32 11 | BufSize uint16 12 | RingId uint16 13 | Direction uint16 14 | Head uint32 15 | Cur uint32 16 | Tail uint32 17 | Flags uint32 18 | pad0 [4]byte 19 | Ts syscall.Timeval 20 | pad1 [72]byte 21 | Sem [128]uint8 22 | Slots Slot // NmSlot is here 23 | } 24 | 25 | func (r *NetmapRing) GetSlots() *[]Slot { 26 | return (*[]Slot)(PtrSliceFrom(unsafe.Pointer(&r.Slots), int(r.NumSlots))) 27 | } 28 | 29 | func (r *NetmapRing) Slot(slotIdx uint32) *Slot { 30 | slotPtrUnsafe := unsafe.Pointer(uintptr(unsafe.Pointer(&r.Slots)) + unsafe.Sizeof(r.Slots)*uintptr(slotIdx)) 31 | return (*Slot)(slotPtrUnsafe) 32 | } 33 | 34 | func (r *NetmapRing) Base() (uintptr, uintptr) { 35 | BasePtr := uintptr(unsafe.Pointer(r)) + r.BufOffset 36 | BufSize := uintptr(r.BufSize) 37 | return BasePtr, BufSize 38 | 39 | } 40 | 41 | func (r *NetmapRing) Next(i uint32) uint32 { 42 | i++ 43 | 44 | if i == r.NumSlots { 45 | i = 0 46 | } 47 | r.Cur = i 48 | r.Head = i 49 | return i 50 | } 51 | 52 | func (r *NetmapRing) GetAvail() uint32 { 53 | if r.Tail < r.Cur { 54 | return r.Tail - r.Cur + r.NumSlots 55 | } else { 56 | return r.Tail - r.Cur 57 | } 58 | } 59 | 60 | func (r *NetmapRing) RingIsEmpty() bool { 61 | return r.Cur == r.Tail 62 | 63 | } 64 | 65 | func (r *NetmapRing) SlotBuffer(slotPtr *Slot) unsafe.Pointer { 66 | idx := uintptr(slotPtr.Idx) 67 | BasePtr, BufSize := r.Base() 68 | return unsafe.Pointer(BasePtr + idx*BufSize) 69 | } 70 | 71 | func (r *NetmapRing) BufferSlice(slotPtr *Slot) *[]byte { 72 | return (*[]byte)(PtrSliceFrom(r.SlotBuffer(slotPtr), int(slotPtr.Len))) 73 | } 74 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package gonetmap 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | func PtrSliceFrom(p unsafe.Pointer, s int) unsafe.Pointer { 11 | return unsafe.Pointer(&reflect.SliceHeader{Data: uintptr(p), Len: s, Cap: s}) 12 | } 13 | 14 | func ifaceTOuint16(i interface{}) uint16 { 15 | var idx uint16 16 | switch i := i.(type) { 17 | case int: 18 | idx = uint16(i) 19 | case int16: 20 | idx = uint16(i) 21 | case int32: 22 | idx = uint16(i) 23 | case int64: 24 | idx = uint16(i) 25 | 26 | case uint: 27 | idx = uint16(i) 28 | case uint16: 29 | idx = uint16(i) 30 | case uint32: 31 | idx = uint16(i) 32 | case uint64: 33 | idx = uint16(i) 34 | } 35 | return idx 36 | } 37 | 38 | func ifaceTOuint32(i interface{}) uint32 { 39 | var idx uint32 40 | switch i := i.(type) { 41 | case int: 42 | idx = uint32(i) 43 | case int16: 44 | idx = uint32(i) 45 | case int32: 46 | idx = uint32(i) 47 | case int64: 48 | idx = uint32(i) 49 | 50 | case uint: 51 | idx = uint32(i) 52 | case uint16: 53 | idx = uint32(i) 54 | case uint32: 55 | idx = uint32(i) 56 | case uint64: 57 | idx = uint32(i) 58 | } 59 | return idx 60 | } 61 | 62 | func nmMmap(file *os.File, r *Request) (ptr uintptr, err error) { 63 | fd := int(file.Fd()) 64 | prot := syscall.PROT_READ | syscall.PROT_WRITE 65 | if data, err := syscall.Mmap(fd, 0, int(r.Memsize), prot, syscall.MAP_SHARED); err == nil { 66 | ptr = (*reflect.SliceHeader)(unsafe.Pointer(&data)).Data 67 | } 68 | return 69 | } 70 | --------------------------------------------------------------------------------