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