github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/link/rawfile/rawfile_unsafe.go (about) 1 // Copyright 2018 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 16 // +build linux 17 18 // Package rawfile contains utilities for using the netstack with raw host 19 // files on Linux hosts. 20 package rawfile 21 22 import ( 23 "reflect" 24 "unsafe" 25 26 "golang.org/x/sys/unix" 27 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip" 28 ) 29 30 // SizeofIovec is the size of a unix.Iovec in bytes. 31 const SizeofIovec = unsafe.Sizeof(unix.Iovec{}) 32 33 // MaxIovs is UIO_MAXIOV, the maximum number of iovecs that may be passed to a 34 // host system call in a single array. 35 const MaxIovs = 1024 36 37 // IovecFromBytes returns a unix.Iovec representing bs. 38 // 39 // Preconditions: len(bs) > 0. 40 func IovecFromBytes(bs []byte) unix.Iovec { 41 iov := unix.Iovec{ 42 Base: &bs[0], 43 } 44 iov.SetLen(len(bs)) 45 return iov 46 } 47 48 func bytesFromIovec(iov unix.Iovec) (bs []byte) { 49 sh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) 50 sh.Data = uintptr(unsafe.Pointer(iov.Base)) 51 sh.Len = int(iov.Len) 52 sh.Cap = int(iov.Len) 53 return 54 } 55 56 // AppendIovecFromBytes returns append(iovs, IovecFromBytes(bs)). If len(bs) == 57 // 0, AppendIovecFromBytes returns iovs without modification. If len(iovs) >= 58 // max, AppendIovecFromBytes replaces the final iovec in iovs with one that 59 // also includes the contents of bs. Note that this implies that 60 // AppendIovecFromBytes is only usable when the returned iovec slice is used as 61 // the source of a write. 62 func AppendIovecFromBytes(iovs []unix.Iovec, bs []byte, max int) []unix.Iovec { 63 if len(bs) == 0 { 64 return iovs 65 } 66 if len(iovs) < max { 67 return append(iovs, IovecFromBytes(bs)) 68 } 69 iovs[len(iovs)-1] = IovecFromBytes(append(bytesFromIovec(iovs[len(iovs)-1]), bs...)) 70 return iovs 71 } 72 73 // MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux. 74 type MMsgHdr struct { 75 Msg unix.Msghdr 76 Len uint32 77 _ [4]byte 78 } 79 80 // SizeofMMsgHdr is the size of a MMsgHdr in bytes. 81 const SizeofMMsgHdr = unsafe.Sizeof(MMsgHdr{}) 82 83 // GetMTU determines the MTU of a network interface device. 84 func GetMTU(name string) (uint32, error) { 85 fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_DGRAM, 0) 86 if err != nil { 87 return 0, err 88 } 89 90 defer unix.Close(fd) 91 92 var ifreq struct { 93 name [16]byte 94 mtu int32 95 _ [20]byte 96 } 97 98 copy(ifreq.name[:], name) 99 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) 100 if errno != 0 { 101 return 0, errno 102 } 103 104 return uint32(ifreq.mtu), nil 105 } 106 107 // NonBlockingWrite writes the given buffer to a file descriptor. It fails if 108 // partial data is written. 109 func NonBlockingWrite(fd int, buf []byte) tcpip.Error { 110 var ptr unsafe.Pointer 111 if len(buf) > 0 { 112 ptr = unsafe.Pointer(&buf[0]) 113 } 114 115 _, _, e := unix.RawSyscall(unix.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf))) 116 if e != 0 { 117 return TranslateErrno(e) 118 } 119 120 return nil 121 } 122 123 // NonBlockingWriteIovec writes iovec to a file descriptor in a single unix. 124 // It fails if partial data is written. 125 func NonBlockingWriteIovec(fd int, iovec []unix.Iovec) tcpip.Error { 126 iovecLen := uintptr(len(iovec)) 127 _, _, e := unix.RawSyscall(unix.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovec[0])), iovecLen) 128 if e != 0 { 129 return TranslateErrno(e) 130 } 131 return nil 132 } 133 134 // NonBlockingSendMMsg sends multiple messages on a socket. 135 func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, tcpip.Error) { 136 n, _, e := unix.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), unix.MSG_DONTWAIT, 0, 0) 137 if e != 0 { 138 return 0, TranslateErrno(e) 139 } 140 141 return int(n), nil 142 } 143 144 // PollEvent represents the pollfd structure passed to a poll() system call. 145 type PollEvent struct { 146 FD int32 147 Events int16 148 Revents int16 149 } 150 151 // BlockingRead reads from a file descriptor that is set up as non-blocking. If 152 // no data is available, it will block in a poll() syscall until the file 153 // descriptor becomes readable. 154 func BlockingRead(fd int, b []byte) (int, tcpip.Error) { 155 n, err := BlockingReadUntranslated(fd, b) 156 if err != 0 { 157 return n, TranslateErrno(err) 158 } 159 return n, nil 160 } 161 162 // BlockingReadUntranslated reads from a file descriptor that is set up as 163 // non-blocking. If no data is available, it will block in a poll() syscall 164 // until the file descriptor becomes readable. It returns the raw unix.Errno 165 // value returned by the underlying syscalls. 166 func BlockingReadUntranslated(fd int, b []byte) (int, unix.Errno) { 167 for { 168 n, _, e := unix.RawSyscall(unix.SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))) 169 if e == 0 { 170 return int(n), 0 171 } 172 173 event := PollEvent{ 174 FD: int32(fd), 175 Events: 1, // POLLIN 176 } 177 178 _, e = BlockingPoll(&event, 1, nil) 179 if e != 0 && e != unix.EINTR { 180 return 0, e 181 } 182 } 183 } 184 185 // BlockingReadvUntilStopped reads from a file descriptor that is set up as 186 // non-blocking and stores the data in a list of iovecs buffers. If no data is 187 // available, it will block in a poll() syscall until the file descriptor 188 // becomes readable or stop is signalled (efd becomes readable). Returns -1 in 189 // the latter case. 190 func BlockingReadvUntilStopped(efd int, fd int, iovecs []unix.Iovec) (int, tcpip.Error) { 191 for { 192 n, _, e := unix.RawSyscall(unix.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs))) 193 if e == 0 { 194 return int(n), nil 195 } 196 if e != 0 && e != unix.EWOULDBLOCK { 197 return 0, TranslateErrno(e) 198 } 199 stopped, e := BlockingPollUntilStopped(efd, fd, unix.POLLIN) 200 if stopped { 201 return -1, nil 202 } 203 if e != 0 && e != unix.EINTR { 204 return 0, TranslateErrno(e) 205 } 206 } 207 } 208 209 // BlockingRecvMMsgUntilStopped reads from a file descriptor that is set up as 210 // non-blocking and stores the received messages in a slice of MMsgHdr 211 // structures. If no data is available, it will block in a poll() syscall until 212 // the file descriptor becomes readable or stop is signalled (efd becomes 213 // readable). Returns -1 in the latter case. 214 func BlockingRecvMMsgUntilStopped(efd int, fd int, msgHdrs []MMsgHdr) (int, tcpip.Error) { 215 for { 216 n, _, e := unix.RawSyscall6(unix.SYS_RECVMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), unix.MSG_DONTWAIT, 0, 0) 217 if e == 0 { 218 return int(n), nil 219 } 220 221 if e != 0 && e != unix.EWOULDBLOCK { 222 return 0, TranslateErrno(e) 223 } 224 225 stopped, e := BlockingPollUntilStopped(efd, fd, unix.POLLIN) 226 if stopped { 227 return -1, nil 228 } 229 if e != 0 && e != unix.EINTR { 230 return 0, TranslateErrno(e) 231 } 232 } 233 } 234 235 // BlockingPollUntilStopped polls for events on fd or until a stop is signalled 236 // on the event fd efd. Returns true if stopped, i.e., efd has event POLLIN. 237 func BlockingPollUntilStopped(efd int, fd int, events int16) (bool, unix.Errno) { 238 pevents := [...]PollEvent{ 239 { 240 FD: int32(efd), 241 Events: unix.POLLIN, 242 }, 243 { 244 FD: int32(fd), 245 Events: events, 246 }, 247 } 248 _, _, errno := unix.Syscall6(unix.SYS_PPOLL, uintptr(unsafe.Pointer(&pevents[0])), uintptr(len(pevents)), 0, 0, 0, 0) 249 if errno != 0 { 250 return pevents[0].Revents&unix.POLLIN != 0, errno 251 } 252 253 if pevents[1].Revents&unix.POLLHUP != 0 || pevents[1].Revents&unix.POLLERR != 0 { 254 errno = unix.ECONNRESET 255 } 256 257 return pevents[0].Revents&unix.POLLIN != 0, errno 258 }