github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/lisafs/channel.go (about) 1 // Copyright 2021 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 lisafs 16 17 import ( 18 "fmt" 19 "math" 20 "runtime" 21 22 "golang.org/x/sys/unix" 23 "github.com/sagernet/gvisor/pkg/fdchannel" 24 "github.com/sagernet/gvisor/pkg/flipcall" 25 "github.com/sagernet/gvisor/pkg/log" 26 ) 27 28 var ( 29 chanHeaderLen = uint32((*channelHeader)(nil).SizeBytes()) 30 ) 31 32 // maxChannels returns the number of channels a client can create. 33 // 34 // The server will reject channel creation requests beyond this (per client). 35 // Note that we don't want the number of channels to be too large, because each 36 // accounts for a large region of shared memory. 37 // TODO(gvisor.dev/issue/6313): Tune the number of channels. 38 func maxChannels() int { 39 maxChans := runtime.GOMAXPROCS(0) 40 if maxChans < 2 { 41 maxChans = 2 42 } 43 if maxChans > 4 { 44 maxChans = 4 45 } 46 return maxChans 47 } 48 49 // channel implements Communicator and represents the communication endpoint 50 // for the client and server and is used to perform fast IPC. Apart from 51 // communicating data, a channel is also capable of donating file descriptors. 52 type channel struct { 53 fdTracker 54 dead bool 55 data flipcall.Endpoint 56 fdChan fdchannel.Endpoint 57 } 58 59 var _ Communicator = (*channel)(nil) 60 61 // PayloadBuf implements Communicator.PayloadBuf. 62 func (ch *channel) PayloadBuf(size uint32) []byte { 63 return ch.data.Data()[chanHeaderLen : chanHeaderLen+size] 64 } 65 66 // SndRcvMessage implements Communicator.SndRcvMessage. 67 func (ch *channel) SndRcvMessage(m MID, payloadLen uint32, wantFDs uint8) (MID, uint32, error) { 68 // Write header. Requests can not donate FDs. 69 ch.marshalHdr(m, 0 /* numFDs */) 70 71 // One-shot communication. RPCs are expected to be quick rather than block. 72 rcvDataLen, err := ch.data.SendRecvFast(chanHeaderLen + payloadLen) 73 if err != nil { 74 // This channel is now unusable. 75 ch.dead = true 76 // Map the transport errors to EIO, but also log the real error. 77 log.Warningf("channel.SndRcvMessage: flipcall.Endpoint.SendRecv failed: %v", err) 78 return 0, 0, unix.EIO 79 } 80 81 return ch.rcvMsg(rcvDataLen) 82 } 83 84 // String implements fmt.Stringer.String. 85 func (ch *channel) String() string { 86 return fmt.Sprintf("channel %p", ch) 87 } 88 89 func (ch *channel) shutdown() { 90 ch.data.Shutdown() 91 } 92 93 func (ch *channel) destroy() { 94 ch.dead = true 95 ch.fdChan.Destroy() 96 ch.data.Destroy() 97 } 98 99 // createChannel creates a server side channel. It returns a packet window 100 // descriptor (for the data channel) and an open socket for the FD channel. 101 func (c *Connection) createChannel(maxMessageSize uint32) (*channel, flipcall.PacketWindowDescriptor, int, error) { 102 c.channelsMu.Lock() 103 defer c.channelsMu.Unlock() 104 // If c.channels is nil, the connection has closed. 105 if c.channels == nil { 106 return nil, flipcall.PacketWindowDescriptor{}, -1, unix.ENOSYS 107 } 108 // Return ENOMEM to indicate that the server has hit its max channels limit. 109 if len(c.channels) >= maxChannels() { 110 return nil, flipcall.PacketWindowDescriptor{}, -1, unix.ENOMEM 111 } 112 ch := &channel{} 113 114 // Set up data channel. 115 desc, err := c.channelAlloc.Allocate(flipcall.PacketHeaderBytes + int(chanHeaderLen+maxMessageSize)) 116 if err != nil { 117 return nil, flipcall.PacketWindowDescriptor{}, -1, err 118 } 119 if err := ch.data.Init(flipcall.ServerSide, desc); err != nil { 120 return nil, flipcall.PacketWindowDescriptor{}, -1, err 121 } 122 123 // Set up FD channel. 124 fdSocks, err := fdchannel.NewConnectedSockets() 125 if err != nil { 126 ch.data.Destroy() 127 return nil, flipcall.PacketWindowDescriptor{}, -1, err 128 } 129 ch.fdChan.Init(fdSocks[0]) 130 clientFDSock := fdSocks[1] 131 132 c.channels = append(c.channels, ch) 133 return ch, desc, clientFDSock, nil 134 } 135 136 // sendFDs sends as many FDs as it can. The failure to send an FD does not 137 // cause an error and fail the entire RPC. FDs are considered supplementary 138 // responses that are not critical to the RPC response itself. The failure to 139 // send the (i)th FD will cause all the following FDs to not be sent as well 140 // because the order in which FDs are donated is important. 141 func (ch *channel) sendFDs(fds []int) uint8 { 142 numFDs := len(fds) 143 if numFDs == 0 { 144 return 0 145 } 146 147 if numFDs > math.MaxUint8 { 148 log.Warningf("dropping all FDs because too many FDs to donate: %v", numFDs) 149 return 0 150 } 151 152 for i, fd := range fds { 153 if err := ch.fdChan.SendFD(fd); err != nil { 154 log.Warningf("error occurred while sending (%d/%d)th FD on channel(%p): %v", i+1, numFDs, ch, err) 155 return uint8(i) 156 } 157 } 158 return uint8(numFDs) 159 } 160 161 // channelHeader is the header present in front of each message received on 162 // flipcall endpoint when the protocol version being used is 1. 163 // 164 // +marshal 165 type channelHeader struct { 166 message MID 167 numFDs uint8 168 _ uint8 // Need to make struct packed. 169 } 170 171 func (ch *channel) marshalHdr(m MID, numFDs uint8) { 172 header := &channelHeader{ 173 message: m, 174 numFDs: numFDs, 175 } 176 header.MarshalUnsafe(ch.data.Data()) 177 } 178 179 func (ch *channel) rcvMsg(dataLen uint32) (MID, uint32, error) { 180 if dataLen < chanHeaderLen { 181 log.Warningf("received data has size smaller than header length: %d", dataLen) 182 return 0, 0, unix.EIO 183 } 184 185 // Read header first. 186 var header channelHeader 187 header.UnmarshalUnsafe(ch.data.Data()) 188 189 // Read any FDs. 190 for i := 0; i < int(header.numFDs); i++ { 191 fd, err := ch.fdChan.RecvFDNonblock() 192 if err != nil { 193 log.Warningf("expected %d FDs, received %d successfully, got err after that: %v", header.numFDs, i, err) 194 break 195 } 196 ch.TrackFD(fd) 197 } 198 199 return header.message, dataLen - chanHeaderLen, nil 200 }