| // Copyright 2018 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package waitable provides the implementation of data-link layer endpoints |
| // that wrap other endpoints, and can wait for inflight calls to WritePacket or |
| // DeliverNetworkPacket to finish (and new ones to be prevented). |
| // |
| // Waitable endpoints can be used in the networking stack by calling New(eID) to |
| // create a new endpoint, where eID is the ID of the endpoint being wrapped, |
| // and then passing it as an argument to Stack.CreateNIC(). |
| package waitable |
| |
| import ( |
| "github.com/google/netstack/gate" |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/buffer" |
| "github.com/google/netstack/tcpip/stack" |
| ) |
| |
| // Endpoint is a waitable link-layer endpoint. |
| type Endpoint struct { |
| dispatchGate gate.Gate |
| dispatcher stack.NetworkDispatcher |
| |
| writeGate gate.Gate |
| lower stack.LinkEndpoint |
| } |
| |
| // New creates a new waitable link-layer endpoint. It wraps around another |
| // endpoint and allows the caller to block new write/dispatch calls and wait for |
| // the inflight ones to finish before returning. |
| func New(lower tcpip.LinkEndpointID) (tcpip.LinkEndpointID, *Endpoint) { |
| e := &Endpoint{ |
| lower: stack.FindLinkEndpoint(lower), |
| } |
| return stack.RegisterLinkEndpoint(e), e |
| } |
| |
| // DeliverNetworkPacket implements stack.NetworkDispatcher.DeliverNetworkPacket. |
| // It is called by the link-layer endpoint being wrapped when a packet arrives, |
| // and only forwards to the actual dispatcher if Wait or WaitDispatch haven't |
| // been called. |
| func (e *Endpoint) DeliverNetworkPacket(linkEP stack.LinkEndpoint, remoteLinkAddr, localLinkAddress tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) { |
| if !e.dispatchGate.Enter() { |
| return |
| } |
| |
| e.dispatcher.DeliverNetworkPacket(e, remoteLinkAddr, localLinkAddress, protocol, vv) |
| e.dispatchGate.Leave() |
| } |
| |
| // Attach implements stack.LinkEndpoint.Attach. It saves the dispatcher and |
| // registers with the lower endpoint as its dispatcher so that "e" is called |
| // for inbound packets. |
| func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) { |
| e.dispatcher = dispatcher |
| e.lower.Attach(e) |
| } |
| |
| // IsAttached implements stack.LinkEndpoint.IsAttached. |
| func (e *Endpoint) IsAttached() bool { |
| return e.dispatcher != nil |
| } |
| |
| // MTU implements stack.LinkEndpoint.MTU. It just forwards the request to the |
| // lower endpoint. |
| func (e *Endpoint) MTU() uint32 { |
| return e.lower.MTU() |
| } |
| |
| // Capabilities implements stack.LinkEndpoint.Capabilities. It just forwards the |
| // request to the lower endpoint. |
| func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities { |
| return e.lower.Capabilities() |
| } |
| |
| // MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength. It just |
| // forwards the request to the lower endpoint. |
| func (e *Endpoint) MaxHeaderLength() uint16 { |
| return e.lower.MaxHeaderLength() |
| } |
| |
| // LinkAddress implements stack.LinkEndpoint.LinkAddress. It just forwards the |
| // request to the lower endpoint. |
| func (e *Endpoint) LinkAddress() tcpip.LinkAddress { |
| return e.lower.LinkAddress() |
| } |
| |
| // WritePacket implements stack.LinkEndpoint.WritePacket. It is called by |
| // higher-level protocols to write packets. It only forwards packets to the |
| // lower endpoint if Wait or WaitWrite haven't been called. |
| func (e *Endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error { |
| if !e.writeGate.Enter() { |
| return nil |
| } |
| |
| err := e.lower.WritePacket(r, hdr, payload, protocol) |
| e.writeGate.Leave() |
| return err |
| } |
| |
| // WaitWrite prevents new calls to WritePacket from reaching the lower endpoint, |
| // and waits for inflight ones to finish before returning. |
| func (e *Endpoint) WaitWrite() { |
| e.writeGate.Close() |
| } |
| |
| // WaitDispatch prevents new calls to DeliverNetworkPacket from reaching the |
| // actual dispatcher, and waits for inflight ones to finish before returning. |
| func (e *Endpoint) WaitDispatch() { |
| e.dispatchGate.Close() |
| } |