github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/link/sharedmem/queue/rx.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 // Package queue provides the implementation of transmit and receive queues 16 // based on shared memory ring buffers. 17 package queue 18 19 import ( 20 "encoding/binary" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops" 23 "github.com/nicocha30/gvisor-ligolo/pkg/log" 24 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/link/sharedmem/pipe" 25 ) 26 27 const ( 28 // Offsets within a posted buffer. 29 postedOffset = 0 30 postedSize = 8 31 postedRemainingInGroup = 12 32 postedUserData = 16 33 postedID = 24 34 35 sizeOfPostedBuffer = 32 36 37 // Offsets within a received packet header. 38 consumedPacketSize = 0 39 consumedPacketReserved = 4 40 41 sizeOfConsumedPacketHeader = 8 42 43 // Offsets within a consumed buffer. 44 consumedOffset = 0 45 consumedSize = 8 46 consumedUserData = 12 47 consumedID = 20 48 49 sizeOfConsumedBuffer = 28 50 51 // The following are the allowed states of the shared data area. 52 // EventFDUinitialized is the value stored at the start of the shared data 53 // region when it hasn't been initialized. 54 EventFDUninitialized = 0 55 // EventFDDisabled is the value stored at the start of the shared data region 56 // when notifications using eventFD has been disabled. 57 EventFDDisabled = 1 58 // EventFDEnabled is the value stored at the start of the shared data region 59 // when eventFD should be notified as the peer might be blocked waiting on 60 // notifications. 61 EventFDEnabled = 2 62 ) 63 64 // RxBuffer is the descriptor of a receive buffer. 65 type RxBuffer struct { 66 Offset uint64 67 Size uint32 68 ID uint64 69 UserData uint64 70 } 71 72 // Rx is a receive queue. It is implemented with one tx and one rx pipe: the tx 73 // pipe is used to "post" buffers, while the rx pipe is used to receive packets 74 // whose contents have been written to previously posted buffers. 75 // 76 // This struct is thread-compatible. 77 type Rx struct { 78 tx pipe.Tx 79 rx pipe.Rx 80 sharedEventFDState *atomicbitops.Uint32 81 } 82 83 // Init initializes the receive queue with the given pipes, and shared state 84 // pointer -- the latter is used to enable/disable eventfd notifications. 85 func (r *Rx) Init(tx, rx []byte, sharedEventFDState *atomicbitops.Uint32) { 86 r.sharedEventFDState = sharedEventFDState 87 r.tx.Init(tx) 88 r.rx.Init(rx) 89 } 90 91 // EnableNotification updates the shared state such that the peer will notify 92 // the eventfd when there are packets to be dequeued. 93 func (r *Rx) EnableNotification() { 94 r.sharedEventFDState.Store(EventFDEnabled) 95 } 96 97 // DisableNotification updates the shared state such that the peer will not 98 // notify the eventfd. 99 func (r *Rx) DisableNotification() { 100 r.sharedEventFDState.Store(EventFDDisabled) 101 } 102 103 // PostedBuffersLimit returns the maximum number of buffers that can be posted 104 // before the tx queue fills up. 105 func (r *Rx) PostedBuffersLimit() uint64 { 106 return r.tx.Capacity(sizeOfPostedBuffer) 107 } 108 109 // PostBuffers makes the given buffers available for receiving data from the 110 // peer. Once they are posted, the peer is free to write to them and will 111 // eventually post them back for consumption. 112 func (r *Rx) PostBuffers(buffers []RxBuffer) bool { 113 for i := range buffers { 114 b := r.tx.Push(sizeOfPostedBuffer) 115 if b == nil { 116 r.tx.Abort() 117 return false 118 } 119 120 pb := &buffers[i] 121 binary.LittleEndian.PutUint64(b[postedOffset:], pb.Offset) 122 binary.LittleEndian.PutUint32(b[postedSize:], pb.Size) 123 binary.LittleEndian.PutUint32(b[postedRemainingInGroup:], 0) 124 binary.LittleEndian.PutUint64(b[postedUserData:], pb.UserData) 125 binary.LittleEndian.PutUint64(b[postedID:], pb.ID) 126 } 127 128 r.tx.Flush() 129 return true 130 } 131 132 // Dequeue receives buffers that have been previously posted by PostBuffers() 133 // and that have been filled by the peer and posted back. 134 // 135 // This is similar to append() in that new buffers are appended to "bufs", with 136 // reallocation only if "bufs" doesn't have enough capacity. 137 func (r *Rx) Dequeue(bufs []RxBuffer) ([]RxBuffer, uint32) { 138 for { 139 outBufs := bufs 140 // Pull the next descriptor from the rx pipe. 141 b := r.rx.Pull() 142 if b == nil { 143 return bufs, 0 144 } 145 146 if len(b) < sizeOfConsumedPacketHeader { 147 log.Warningf("Ignoring packet header: size (%v) is less than header size (%v)", len(b), sizeOfConsumedPacketHeader) 148 r.rx.Flush() 149 continue 150 } 151 152 totalDataSize := binary.LittleEndian.Uint32(b[consumedPacketSize:]) 153 154 // Calculate the number of buffer descriptors and copy them 155 // over to the output. 156 count := (len(b) - sizeOfConsumedPacketHeader) / sizeOfConsumedBuffer 157 offset := sizeOfConsumedPacketHeader 158 buffersSize := uint32(0) 159 for i := count; i > 0; i-- { 160 s := binary.LittleEndian.Uint32(b[offset+consumedSize:]) 161 buffersSize += s 162 if buffersSize < s { 163 // The buffer size overflows an unsigned 32-bit 164 // integer, so break out and force it to be 165 // ignored. 166 totalDataSize = 1 167 buffersSize = 0 168 break 169 } 170 171 outBufs = append(outBufs, RxBuffer{ 172 Offset: binary.LittleEndian.Uint64(b[offset+consumedOffset:]), 173 Size: s, 174 ID: binary.LittleEndian.Uint64(b[offset+consumedID:]), 175 }) 176 177 offset += sizeOfConsumedBuffer 178 } 179 180 r.rx.Flush() 181 182 if buffersSize < totalDataSize { 183 // The descriptor is corrupted, ignore it. 184 log.Warningf("Ignoring packet: actual data size (%v) less than expected size (%v)", buffersSize, totalDataSize) 185 continue 186 } 187 188 return outBufs, totalDataSize 189 } 190 } 191 192 // Bytes returns the byte slices on which the queue operates. 193 func (r *Rx) Bytes() (tx, rx []byte) { 194 return r.tx.Bytes(), r.rx.Bytes() 195 } 196 197 // DecodeRxBufferHeader decodes the header of a buffer posted on an rx queue. 198 func DecodeRxBufferHeader(b []byte) RxBuffer { 199 return RxBuffer{ 200 Offset: binary.LittleEndian.Uint64(b[postedOffset:]), 201 Size: binary.LittleEndian.Uint32(b[postedSize:]), 202 ID: binary.LittleEndian.Uint64(b[postedID:]), 203 UserData: binary.LittleEndian.Uint64(b[postedUserData:]), 204 } 205 } 206 207 // RxCompletionSize returns the number of bytes needed to encode an rx 208 // completion containing "count" buffers. 209 func RxCompletionSize(count int) uint64 { 210 return sizeOfConsumedPacketHeader + uint64(count)*sizeOfConsumedBuffer 211 } 212 213 // EncodeRxCompletion encodes an rx completion header. 214 func EncodeRxCompletion(b []byte, size, reserved uint32) { 215 binary.LittleEndian.PutUint32(b[consumedPacketSize:], size) 216 binary.LittleEndian.PutUint32(b[consumedPacketReserved:], reserved) 217 } 218 219 // EncodeRxCompletionBuffer encodes the i-th rx completion buffer header. 220 func EncodeRxCompletionBuffer(b []byte, i int, rxb RxBuffer) { 221 b = b[RxCompletionSize(i):] 222 binary.LittleEndian.PutUint64(b[consumedOffset:], rxb.Offset) 223 binary.LittleEndian.PutUint32(b[consumedSize:], rxb.Size) 224 binary.LittleEndian.PutUint64(b[consumedUserData:], rxb.UserData) 225 binary.LittleEndian.PutUint64(b[consumedID:], rxb.ID) 226 }