github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/link/qdisc/fifo/endpoint.go (about)

     1  // Copyright 2020 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 fifo provides the implementation of data-link layer endpoints that
    16  // wrap another endpoint and queues all outbound packets and asynchronously
    17  // dispatches them to the lower endpoint.
    18  package fifo
    19  
    20  import (
    21  	"github.com/SagerNet/gvisor/pkg/sleep"
    22  	"github.com/SagerNet/gvisor/pkg/sync"
    23  	"github.com/SagerNet/gvisor/pkg/tcpip"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    26  )
    27  
    28  var _ stack.LinkEndpoint = (*endpoint)(nil)
    29  var _ stack.GSOEndpoint = (*endpoint)(nil)
    30  
    31  // endpoint represents a LinkEndpoint which implements a FIFO queue for all
    32  // outgoing packets. endpoint can have 1 or more underlying queueDispatchers.
    33  // All outgoing packets are consistenly hashed to a single underlying queue
    34  // using the PacketBuffer.Hash if set, otherwise all packets are queued to the
    35  // first queue to avoid reordering in case of missing hash.
    36  type endpoint struct {
    37  	dispatcher  stack.NetworkDispatcher
    38  	lower       stack.LinkEndpoint
    39  	wg          sync.WaitGroup
    40  	dispatchers []*queueDispatcher
    41  }
    42  
    43  // queueDispatcher is responsible for dispatching all outbound packets in its
    44  // queue. It will also smartly batch packets when possible and write them
    45  // through the lower LinkEndpoint.
    46  type queueDispatcher struct {
    47  	lower          stack.LinkEndpoint
    48  	q              *packetBufferQueue
    49  	newPacketWaker sleep.Waker
    50  	closeWaker     sleep.Waker
    51  }
    52  
    53  // New creates a new fifo link endpoint with the n queues with maximum
    54  // capacity of queueLen.
    55  func New(lower stack.LinkEndpoint, n int, queueLen int) stack.LinkEndpoint {
    56  	e := &endpoint{
    57  		lower: lower,
    58  	}
    59  	// Create the required dispatchers
    60  	for i := 0; i < n; i++ {
    61  		qd := &queueDispatcher{
    62  			q:     &packetBufferQueue{limit: queueLen},
    63  			lower: lower,
    64  		}
    65  		e.dispatchers = append(e.dispatchers, qd)
    66  		e.wg.Add(1)
    67  		go func() {
    68  			defer e.wg.Done()
    69  			qd.dispatchLoop()
    70  		}()
    71  	}
    72  	return e
    73  }
    74  
    75  func (q *queueDispatcher) dispatchLoop() {
    76  	const newPacketWakerID = 1
    77  	const closeWakerID = 2
    78  	s := sleep.Sleeper{}
    79  	s.AddWaker(&q.newPacketWaker, newPacketWakerID)
    80  	s.AddWaker(&q.closeWaker, closeWakerID)
    81  	defer s.Done()
    82  
    83  	const batchSize = 32
    84  	var batch stack.PacketBufferList
    85  	for {
    86  		id, ok := s.Fetch(true)
    87  		if ok && id == closeWakerID {
    88  			return
    89  		}
    90  		for pkt := q.q.dequeue(); pkt != nil; pkt = q.q.dequeue() {
    91  			batch.PushBack(pkt)
    92  			if batch.Len() < batchSize && !q.q.empty() {
    93  				continue
    94  			}
    95  			// We pass a protocol of zero here because each packet carries its
    96  			// NetworkProtocol.
    97  			q.lower.WritePackets(stack.RouteInfo{}, batch, 0 /* protocol */)
    98  			for pkt := batch.Front(); pkt != nil; pkt = pkt.Next() {
    99  				batch.Remove(pkt)
   100  			}
   101  			batch.Reset()
   102  		}
   103  	}
   104  }
   105  
   106  // DeliverNetworkPacket implements stack.NetworkDispatcher.DeliverNetworkPacket.
   107  func (e *endpoint) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   108  	e.dispatcher.DeliverNetworkPacket(remote, local, protocol, pkt)
   109  }
   110  
   111  // DeliverOutboundPacket implements stack.NetworkDispatcher.DeliverOutboundPacket.
   112  func (e *endpoint) DeliverOutboundPacket(remote, local tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   113  	e.dispatcher.DeliverOutboundPacket(remote, local, protocol, pkt)
   114  }
   115  
   116  // Attach implements stack.LinkEndpoint.Attach.
   117  func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
   118  	e.dispatcher = dispatcher
   119  	e.lower.Attach(e)
   120  }
   121  
   122  // IsAttached implements stack.LinkEndpoint.IsAttached.
   123  func (e *endpoint) IsAttached() bool {
   124  	return e.dispatcher != nil
   125  }
   126  
   127  // MTU implements stack.LinkEndpoint.MTU.
   128  func (e *endpoint) MTU() uint32 {
   129  	return e.lower.MTU()
   130  }
   131  
   132  // Capabilities implements stack.LinkEndpoint.Capabilities.
   133  func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
   134  	return e.lower.Capabilities()
   135  }
   136  
   137  // MaxHeaderLength implements stack.LinkEndpoint.MaxHeaderLength.
   138  func (e *endpoint) MaxHeaderLength() uint16 {
   139  	return e.lower.MaxHeaderLength()
   140  }
   141  
   142  // LinkAddress implements stack.LinkEndpoint.LinkAddress.
   143  func (e *endpoint) LinkAddress() tcpip.LinkAddress {
   144  	return e.lower.LinkAddress()
   145  }
   146  
   147  // GSOMaxSize implements stack.GSOEndpoint.
   148  func (e *endpoint) GSOMaxSize() uint32 {
   149  	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
   150  		return gso.GSOMaxSize()
   151  	}
   152  	return 0
   153  }
   154  
   155  // SupportedGSO implements stack.GSOEndpoint.
   156  func (e *endpoint) SupportedGSO() stack.SupportedGSO {
   157  	if gso, ok := e.lower.(stack.GSOEndpoint); ok {
   158  		return gso.SupportedGSO()
   159  	}
   160  	return stack.GSONotSupported
   161  }
   162  
   163  // WritePacket implements stack.LinkEndpoint.WritePacket.
   164  //
   165  // The packet must have the following fields populated:
   166  //  - pkt.EgressRoute
   167  //  - pkt.GSOOptions
   168  //  - pkt.NetworkProtocolNumber
   169  func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
   170  	d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
   171  	if !d.q.enqueue(pkt) {
   172  		return &tcpip.ErrNoBufferSpace{}
   173  	}
   174  	d.newPacketWaker.Assert()
   175  	return nil
   176  }
   177  
   178  // WritePackets implements stack.LinkEndpoint.WritePackets.
   179  //
   180  // Each packet in the packet buffer list must have the following fields
   181  // populated:
   182  //  - pkt.EgressRoute
   183  //  - pkt.GSOOptions
   184  //  - pkt.NetworkProtocolNumber
   185  func (e *endpoint) WritePackets(r stack.RouteInfo, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
   186  	enqueued := 0
   187  	for pkt := pkts.Front(); pkt != nil; {
   188  		d := e.dispatchers[int(pkt.Hash)%len(e.dispatchers)]
   189  		nxt := pkt.Next()
   190  		if !d.q.enqueue(pkt) {
   191  			if enqueued > 0 {
   192  				d.newPacketWaker.Assert()
   193  			}
   194  			return enqueued, &tcpip.ErrNoBufferSpace{}
   195  		}
   196  		pkt = nxt
   197  		enqueued++
   198  		d.newPacketWaker.Assert()
   199  	}
   200  	return enqueued, nil
   201  }
   202  
   203  // Wait implements stack.LinkEndpoint.Wait.
   204  func (e *endpoint) Wait() {
   205  	e.lower.Wait()
   206  
   207  	// The linkEP is gone. Teardown the outbound dispatcher goroutines.
   208  	for i := range e.dispatchers {
   209  		e.dispatchers[i].closeWaker.Assert()
   210  	}
   211  
   212  	e.wg.Wait()
   213  }
   214  
   215  // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType
   216  func (e *endpoint) ARPHardwareType() header.ARPHardwareType {
   217  	return e.lower.ARPHardwareType()
   218  }
   219  
   220  // AddHeader implements stack.LinkEndpoint.AddHeader.
   221  func (e *endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
   222  	e.lower.AddHeader(local, remote, protocol, pkt)
   223  }