github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/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 "unsafe" 23 24 "golang.org/x/sys/unix" 25 ) 26 27 // tPacketHdrlen is the TPACKET_HDRLEN variable defined in <linux/if_packet.h>. 28 var tPacketHdrlen = tPacketAlign(unsafe.Sizeof(tPacketHdr{}) + unsafe.Sizeof(unix.RawSockaddrLinklayer{})) 29 30 // tpStatus returns the frame status field. 31 // The status is concurrently updated by the kernel as a result we must 32 // use atomic operations to prevent races. 33 func (t tPacketHdr) tpStatus() uint32 { 34 hdr := unsafe.Pointer(&t[0]) 35 statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset)) 36 return atomic.LoadUint32((*uint32)(statusPtr)) 37 } 38 39 // setTPStatus set's the frame status to the provided status. 40 // The status is concurrently updated by the kernel as a result we must 41 // use atomic operations to prevent races. 42 func (t tPacketHdr) setTPStatus(status uint32) { 43 hdr := unsafe.Pointer(&t[0]) 44 statusPtr := unsafe.Pointer(uintptr(hdr) + uintptr(tpStatusOffset)) 45 atomic.StoreUint32((*uint32)(statusPtr), status) 46 } 47 48 func newPacketMMapDispatcher(fd int, e *endpoint) (linkDispatcher, error) { 49 d := &packetMMapDispatcher{ 50 fd: fd, 51 e: e, 52 } 53 pageSize := unix.Getpagesize() 54 if tpBlockSize%pageSize != 0 { 55 return nil, fmt.Errorf("tpBlockSize: %d is not page aligned, pagesize: %d", tpBlockSize, pageSize) 56 } 57 tReq := tPacketReq{ 58 tpBlockSize: uint32(tpBlockSize), 59 tpBlockNR: uint32(tpBlockNR), 60 tpFrameSize: uint32(tpFrameSize), 61 tpFrameNR: uint32(tpFrameNR), 62 } 63 // Setup PACKET_RX_RING. 64 if err := setsockopt(d.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tReq), unsafe.Sizeof(tReq)); err != nil { 65 return nil, fmt.Errorf("failed to enable PACKET_RX_RING: %v", err) 66 } 67 // Let's mmap the blocks. 68 sz := tpBlockSize * tpBlockNR 69 buf, err := unix.Mmap(d.fd, 0, sz, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) 70 if err != nil { 71 return nil, fmt.Errorf("unix.Mmap(...,0, %v, ...) failed = %v", sz, err) 72 } 73 d.ringBuffer = buf 74 return d, nil 75 } 76 77 func setsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error { 78 if _, _, errno := unix.Syscall6(unix.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(val), vallen, 0); errno != 0 { 79 return error(errno) 80 } 81 82 return nil 83 }