github.com/google/netstack@v0.0.0-20191123085552-55fcc16cd0eb/tcpip/link/fdbased/mmap_unsafe.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build linux,amd64 linux,arm64 16 17 package fdbased 18 19 import ( 20 "fmt" 21 "sync/atomic" 22 "syscall" 23 "unsafe" 24 25 "golang.org/x/sys/unix" 26 ) 27 28 // tPacketHdrlen is the TPACKET_HDRLEN variable defined in <linux/if_packet.h>. 29 var tPacketHdrlen = tPacketAlign(unsafe.Sizeof(tPacketHdr{}) + unsafe.Sizeof(syscall.RawSockaddrLinklayer{})) 30 31 // tpStatus returns the frame status field. 32 // The status is concurrently updated by the kernel as a result we must 33 // use atomic operations to prevent races. 34 func (t tPacketHdr) tpStatus() uint32 { 35 hdr := unsafe.Pointer(&t[0]) 36 statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset)) 37 return atomic.LoadUint32((*uint32)(statusPtr)) 38 } 39 40 // setTPStatus set's the frame status to the provided status. 41 // The status is concurrently updated by the kernel as a result we must 42 // use atomic operations to prevent races. 43 func (t tPacketHdr) setTPStatus(status uint32) { 44 hdr := unsafe.Pointer(&t[0]) 45 statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset)) 46 atomic.StoreUint32((*uint32)(statusPtr), status) 47 } 48 49 func newPacketMMapDispatcher(fd int, e *endpoint) (linkDispatcher, error) { 50 d := &packetMMapDispatcher{ 51 fd: fd, 52 e: e, 53 } 54 pageSize := unix.Getpagesize() 55 if tpBlockSize%pageSize != 0 { 56 return nil, fmt.Errorf("tpBlockSize: %d is not page aligned, pagesize: %d", tpBlockSize, pageSize) 57 } 58 tReq := tPacketReq{ 59 tpBlockSize: uint32(tpBlockSize), 60 tpBlockNR: uint32(tpBlockNR), 61 tpFrameSize: uint32(tpFrameSize), 62 tpFrameNR: uint32(tpFrameNR), 63 } 64 // Setup PACKET_RX_RING. 65 if err := setsockopt(d.fd, syscall.SOL_PACKET, syscall.PACKET_RX_RING, unsafe.Pointer(&tReq), unsafe.Sizeof(tReq)); err != nil { 66 return nil, fmt.Errorf("failed to enable PACKET_RX_RING: %v", err) 67 } 68 // Let's mmap the blocks. 69 sz := tpBlockSize * tpBlockNR 70 buf, err := syscall.Mmap(d.fd, 0, sz, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 71 if err != nil { 72 return nil, fmt.Errorf("syscall.Mmap(...,0, %v, ...) failed = %v", sz, err) 73 } 74 d.ringBuffer = buf 75 return d, nil 76 } 77 78 func setsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { 79 if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(val), vallen, 0); errno != 0 { 80 return error(errno) 81 } 82 83 return nil 84 }