inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/tcpip/link/channel/channel.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 channel provides the implemention of channel-based data-link layer 16 // endpoints. Such endpoints allow injection of inbound packets and store 17 // outbound packets in a channel. 18 package channel 19 20 import ( 21 "context" 22 23 "inet.af/netstack/sync" 24 "inet.af/netstack/tcpip" 25 "inet.af/netstack/tcpip/header" 26 "inet.af/netstack/tcpip/stack" 27 ) 28 29 // PacketInfo holds all the information about an outbound packet. 30 type PacketInfo struct { 31 Pkt *stack.PacketBuffer 32 33 // TODO(https://gvisor.dev/issue/6537): Remove these fields. 34 Proto tcpip.NetworkProtocolNumber 35 Route stack.RouteInfo 36 } 37 38 // Notification is the interface for receiving notification from the packet 39 // queue. 40 type Notification interface { 41 // WriteNotify will be called when a write happens to the queue. 42 WriteNotify() 43 } 44 45 // NotificationHandle is an opaque handle to the registered notification target. 46 // It can be used to unregister the notification when no longer interested. 47 // 48 // +stateify savable 49 type NotificationHandle struct { 50 n Notification 51 } 52 53 type queue struct { 54 // c is the outbound packet channel. 55 c chan PacketInfo 56 // mu protects fields below. 57 mu sync.RWMutex 58 notify []*NotificationHandle 59 } 60 61 func (q *queue) Close() { 62 close(q.c) 63 } 64 65 func (q *queue) Read() (PacketInfo, bool) { 66 select { 67 case p := <-q.c: 68 return p, true 69 default: 70 return PacketInfo{}, false 71 } 72 } 73 74 func (q *queue) ReadContext(ctx context.Context) (PacketInfo, bool) { 75 select { 76 case pkt := <-q.c: 77 return pkt, true 78 case <-ctx.Done(): 79 return PacketInfo{}, false 80 } 81 } 82 83 func (q *queue) Write(p PacketInfo) bool { 84 // q holds the PacketBuffer. 85 86 // Ideally, Write() should take a reference here, since it is adding 87 // the underlying PacketBuffer to the channel. However, in practice, 88 // calls to Read() are not necessarily symetric with calls 89 // to Write() (e.g writing to this endpoint and then exiting). This 90 // causes tests and analyzers to detect erroneous "leaks" for expected 91 // behavior. To prevent this, we allow the refcount to go to zero, and 92 // make a call to PreserveObject(), which prevents the PacketBuffer 93 // pooling implementation from reclaiming this instance, even when 94 // the refcount goes to zero. 95 p.Pkt.PreserveObject() 96 wrote := false 97 select { 98 case q.c <- p: 99 wrote = true 100 default: 101 } 102 q.mu.Lock() 103 notify := q.notify 104 q.mu.Unlock() 105 106 if wrote { 107 // Send notification outside of lock. 108 for _, h := range notify { 109 h.n.WriteNotify() 110 } 111 } 112 return wrote 113 } 114 115 func (q *queue) Num() int { 116 return len(q.c) 117 } 118 119 func (q *queue) AddNotify(notify Notification) *NotificationHandle { 120 q.mu.Lock() 121 defer q.mu.Unlock() 122 h := &NotificationHandle{n: notify} 123 q.notify = append(q.notify, h) 124 return h 125 } 126 127 func (q *queue) RemoveNotify(handle *NotificationHandle) { 128 q.mu.Lock() 129 defer q.mu.Unlock() 130 // Make a copy, since we reads the array outside of lock when notifying. 131 notify := make([]*NotificationHandle, 0, len(q.notify)) 132 for _, h := range q.notify { 133 if h != handle { 134 notify = append(notify, h) 135 } 136 } 137 q.notify = notify 138 } 139 140 var _ stack.LinkEndpoint = (*Endpoint)(nil) 141 var _ stack.GSOEndpoint = (*Endpoint)(nil) 142 143 // Endpoint is link layer endpoint that stores outbound packets in a channel 144 // and allows injection of inbound packets. 145 type Endpoint struct { 146 dispatcher stack.NetworkDispatcher 147 mtu uint32 148 linkAddr tcpip.LinkAddress 149 LinkEPCapabilities stack.LinkEndpointCapabilities 150 SupportedGSOKind stack.SupportedGSO 151 152 // Outbound packet queue. 153 q *queue 154 } 155 156 // New creates a new channel endpoint. 157 func New(size int, mtu uint32, linkAddr tcpip.LinkAddress) *Endpoint { 158 return &Endpoint{ 159 q: &queue{ 160 c: make(chan PacketInfo, size), 161 }, 162 mtu: mtu, 163 linkAddr: linkAddr, 164 } 165 } 166 167 // Close closes e. Further packet injections will panic. Reads continue to 168 // succeed until all packets are read. 169 func (e *Endpoint) Close() { 170 e.q.Close() 171 } 172 173 // Read does non-blocking read one packet from the outbound packet queue. 174 func (e *Endpoint) Read() (PacketInfo, bool) { 175 return e.q.Read() 176 } 177 178 // ReadContext does blocking read for one packet from the outbound packet queue. 179 // It can be cancelled by ctx, and in this case, it returns false. 180 func (e *Endpoint) ReadContext(ctx context.Context) (PacketInfo, bool) { 181 return e.q.ReadContext(ctx) 182 } 183 184 // Drain removes all outbound packets from the channel and counts them. 185 func (e *Endpoint) Drain() int { 186 c := 0 187 for { 188 if _, ok := e.Read(); !ok { 189 return c 190 } 191 c++ 192 } 193 } 194 195 // NumQueued returns the number of packet queued for outbound. 196 func (e *Endpoint) NumQueued() int { 197 return e.q.Num() 198 } 199 200 // InjectInbound injects an inbound packet. 201 func (e *Endpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { 202 e.InjectLinkAddr(protocol, "", pkt) 203 } 204 205 // InjectLinkAddr injects an inbound packet with a remote link address. 206 func (e *Endpoint) InjectLinkAddr(protocol tcpip.NetworkProtocolNumber, remote tcpip.LinkAddress, pkt *stack.PacketBuffer) { 207 e.dispatcher.DeliverNetworkPacket(remote, "" /* local */, protocol, pkt) 208 } 209 210 // Attach saves the stack network-layer dispatcher for use later when packets 211 // are injected. 212 func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) { 213 e.dispatcher = dispatcher 214 } 215 216 // IsAttached implements stack.LinkEndpoint.IsAttached. 217 func (e *Endpoint) IsAttached() bool { 218 return e.dispatcher != nil 219 } 220 221 // MTU implements stack.LinkEndpoint.MTU. It returns the value initialized 222 // during construction. 223 func (e *Endpoint) MTU() uint32 { 224 return e.mtu 225 } 226 227 // Capabilities implements stack.LinkEndpoint.Capabilities. 228 func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities { 229 return e.LinkEPCapabilities 230 } 231 232 // GSOMaxSize implements stack.GSOEndpoint. 233 func (*Endpoint) GSOMaxSize() uint32 { 234 return 1 << 15 235 } 236 237 // SupportedGSO implements stack.GSOEndpoint. 238 func (e *Endpoint) SupportedGSO() stack.SupportedGSO { 239 return e.SupportedGSOKind 240 } 241 242 // MaxHeaderLength returns the maximum size of the link layer header. Given it 243 // doesn't have a header, it just returns 0. 244 func (*Endpoint) MaxHeaderLength() uint16 { 245 return 0 246 } 247 248 // LinkAddress returns the link address of this endpoint. 249 func (e *Endpoint) LinkAddress() tcpip.LinkAddress { 250 return e.linkAddr 251 } 252 253 // WritePacket stores outbound packets into the channel. 254 func (e *Endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error { 255 p := PacketInfo{ 256 Pkt: pkt, 257 Proto: protocol, 258 Route: r, 259 } 260 261 // Write returns false if the queue is full. A full queue is not an error 262 // from the perspective of a LinkEndpoint so we ignore Write's return 263 // value and always return nil from this method. 264 _ = e.q.Write(p) 265 266 return nil 267 } 268 269 // WritePackets stores outbound packets into the channel. 270 func (e *Endpoint) WritePackets(r stack.RouteInfo, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) { 271 n := 0 272 for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { 273 p := PacketInfo{ 274 Pkt: pkt, 275 Proto: protocol, 276 Route: r, 277 } 278 279 if !e.q.Write(p) { 280 break 281 } 282 n++ 283 } 284 285 return n, nil 286 } 287 288 // Wait implements stack.LinkEndpoint.Wait. 289 func (*Endpoint) Wait() {} 290 291 // AddNotify adds a notification target for receiving event about outgoing 292 // packets. 293 func (e *Endpoint) AddNotify(notify Notification) *NotificationHandle { 294 return e.q.AddNotify(notify) 295 } 296 297 // RemoveNotify removes handle from the list of notification targets. 298 func (e *Endpoint) RemoveNotify(handle *NotificationHandle) { 299 e.q.RemoveNotify(handle) 300 } 301 302 // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. 303 func (*Endpoint) ARPHardwareType() header.ARPHardwareType { 304 return header.ARPHardwareNone 305 } 306 307 // AddHeader implements stack.LinkEndpoint.AddHeader. 308 func (*Endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) { 309 } 310 311 // WriteRawPacket implements stack.LinkEndpoint. 312 func (e *Endpoint) WriteRawPacket(pkt *stack.PacketBuffer) tcpip.Error { 313 p := PacketInfo{ 314 Pkt: pkt, 315 Proto: pkt.NetworkProtocolNumber, 316 } 317 318 // Write returns false if the queue is full. A full queue is not an error 319 // from the perspective of a LinkEndpoint so we ignore Write's return 320 // value and always return nil from this method. 321 _ = e.q.Write(p) 322 323 return nil 324 }