github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/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  	"github.com/nicocha30/gvisor-ligolo/pkg/sync"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/header"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/stack"
    27  )
    28  
    29  // Notification is the interface for receiving notification from the packet
    30  // queue.
    31  type Notification interface {
    32  	// WriteNotify will be called when a write happens to the queue.
    33  	WriteNotify()
    34  }
    35  
    36  // NotificationHandle is an opaque handle to the registered notification target.
    37  // It can be used to unregister the notification when no longer interested.
    38  //
    39  // +stateify savable
    40  type NotificationHandle struct {
    41  	n Notification
    42  }
    43  
    44  type queue struct {
    45  	// c is the outbound packet channel.
    46  	c  chan stack.PacketBufferPtr
    47  	mu sync.RWMutex
    48  	// +checklocks:mu
    49  	notify []*NotificationHandle
    50  	// +checklocks:mu
    51  	closed bool
    52  }
    53  
    54  func (q *queue) Close() {
    55  	q.mu.Lock()
    56  	defer q.mu.Unlock()
    57  	close(q.c)
    58  	q.closed = true
    59  }
    60  
    61  func (q *queue) Read() stack.PacketBufferPtr {
    62  	select {
    63  	case p := <-q.c:
    64  		return p
    65  	default:
    66  		return nil
    67  	}
    68  }
    69  
    70  func (q *queue) ReadContext(ctx context.Context) stack.PacketBufferPtr {
    71  	select {
    72  	case pkt := <-q.c:
    73  		return pkt
    74  	case <-ctx.Done():
    75  		return nil
    76  	}
    77  }
    78  
    79  func (q *queue) Write(pkt stack.PacketBufferPtr) tcpip.Error {
    80  	// q holds the PacketBuffer.
    81  	q.mu.RLock()
    82  	if q.closed {
    83  		q.mu.RUnlock()
    84  		return &tcpip.ErrClosedForSend{}
    85  	}
    86  
    87  	wrote := false
    88  	select {
    89  	case q.c <- pkt.IncRef():
    90  		wrote = true
    91  	default:
    92  		pkt.DecRef()
    93  	}
    94  	notify := q.notify
    95  	q.mu.RUnlock()
    96  
    97  	if wrote {
    98  		// Send notification outside of lock.
    99  		for _, h := range notify {
   100  			h.n.WriteNotify()
   101  		}
   102  		return nil
   103  	}
   104  	return &tcpip.ErrNoBufferSpace{}
   105  }
   106  
   107  func (q *queue) Num() int {
   108  	return len(q.c)
   109  }
   110  
   111  func (q *queue) AddNotify(notify Notification) *NotificationHandle {
   112  	q.mu.Lock()
   113  	defer q.mu.Unlock()
   114  	h := &NotificationHandle{n: notify}
   115  	q.notify = append(q.notify, h)
   116  	return h
   117  }
   118  
   119  func (q *queue) RemoveNotify(handle *NotificationHandle) {
   120  	q.mu.Lock()
   121  	defer q.mu.Unlock()
   122  	// Make a copy, since we reads the array outside of lock when notifying.
   123  	notify := make([]*NotificationHandle, 0, len(q.notify))
   124  	for _, h := range q.notify {
   125  		if h != handle {
   126  			notify = append(notify, h)
   127  		}
   128  	}
   129  	q.notify = notify
   130  }
   131  
   132  var _ stack.LinkEndpoint = (*Endpoint)(nil)
   133  var _ stack.GSOEndpoint = (*Endpoint)(nil)
   134  
   135  // Endpoint is link layer endpoint that stores outbound packets in a channel
   136  // and allows injection of inbound packets.
   137  type Endpoint struct {
   138  	mtu                uint32
   139  	linkAddr           tcpip.LinkAddress
   140  	LinkEPCapabilities stack.LinkEndpointCapabilities
   141  	SupportedGSOKind   stack.SupportedGSO
   142  
   143  	mu sync.RWMutex
   144  	// +checklocks:mu
   145  	dispatcher stack.NetworkDispatcher
   146  
   147  	// Outbound packet queue.
   148  	q *queue
   149  }
   150  
   151  // New creates a new channel endpoint.
   152  func New(size int, mtu uint32, linkAddr tcpip.LinkAddress) *Endpoint {
   153  	return &Endpoint{
   154  		q: &queue{
   155  			c: make(chan stack.PacketBufferPtr, size),
   156  		},
   157  		mtu:      mtu,
   158  		linkAddr: linkAddr,
   159  	}
   160  }
   161  
   162  // Close closes e. Further packet injections will return an error, and all pending
   163  // packets are discarded. Close may be called concurrently with WritePackets.
   164  func (e *Endpoint) Close() {
   165  	e.q.Close()
   166  	e.Drain()
   167  }
   168  
   169  // Read does non-blocking read one packet from the outbound packet queue.
   170  func (e *Endpoint) Read() stack.PacketBufferPtr {
   171  	return e.q.Read()
   172  }
   173  
   174  // ReadContext does blocking read for one packet from the outbound packet queue.
   175  // It can be cancelled by ctx, and in this case, it returns nil.
   176  func (e *Endpoint) ReadContext(ctx context.Context) stack.PacketBufferPtr {
   177  	return e.q.ReadContext(ctx)
   178  }
   179  
   180  // Drain removes all outbound packets from the channel and counts them.
   181  func (e *Endpoint) Drain() int {
   182  	c := 0
   183  	for pkt := e.Read(); !pkt.IsNil(); pkt = e.Read() {
   184  		pkt.DecRef()
   185  		c++
   186  	}
   187  	return c
   188  }
   189  
   190  // NumQueued returns the number of packet queued for outbound.
   191  func (e *Endpoint) NumQueued() int {
   192  	return e.q.Num()
   193  }
   194  
   195  // InjectInbound injects an inbound packet. If the endpoint is not attached, the
   196  // packet is not delivered.
   197  func (e *Endpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt stack.PacketBufferPtr) {
   198  	e.mu.RLock()
   199  	d := e.dispatcher
   200  	e.mu.RUnlock()
   201  	if d != nil {
   202  		d.DeliverNetworkPacket(protocol, pkt)
   203  	}
   204  }
   205  
   206  // Attach saves the stack network-layer dispatcher for use later when packets
   207  // are injected.
   208  func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
   209  	e.mu.Lock()
   210  	defer e.mu.Unlock()
   211  	e.dispatcher = dispatcher
   212  }
   213  
   214  // IsAttached implements stack.LinkEndpoint.IsAttached.
   215  func (e *Endpoint) IsAttached() bool {
   216  	e.mu.RLock()
   217  	defer e.mu.RUnlock()
   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  // WritePackets stores outbound packets into the channel.
   254  // Multiple concurrent calls are permitted.
   255  func (e *Endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) {
   256  	n := 0
   257  	for _, pkt := range pkts.AsSlice() {
   258  		if err := e.q.Write(pkt); err != nil {
   259  			if _, ok := err.(*tcpip.ErrNoBufferSpace); !ok && n == 0 {
   260  				return 0, err
   261  			}
   262  			break
   263  		}
   264  		n++
   265  	}
   266  
   267  	return n, nil
   268  }
   269  
   270  // Wait implements stack.LinkEndpoint.Wait.
   271  func (*Endpoint) Wait() {}
   272  
   273  // AddNotify adds a notification target for receiving event about outgoing
   274  // packets.
   275  func (e *Endpoint) AddNotify(notify Notification) *NotificationHandle {
   276  	return e.q.AddNotify(notify)
   277  }
   278  
   279  // RemoveNotify removes handle from the list of notification targets.
   280  func (e *Endpoint) RemoveNotify(handle *NotificationHandle) {
   281  	e.q.RemoveNotify(handle)
   282  }
   283  
   284  // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
   285  func (*Endpoint) ARPHardwareType() header.ARPHardwareType {
   286  	return header.ARPHardwareNone
   287  }
   288  
   289  // AddHeader implements stack.LinkEndpoint.AddHeader.
   290  func (*Endpoint) AddHeader(stack.PacketBufferPtr) {}
   291  
   292  // ParseHeader implements stack.LinkEndpoint.ParseHeader.
   293  func (*Endpoint) ParseHeader(stack.PacketBufferPtr) bool { return true }