github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/link/sharedmem/server_rx.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  //go:build linux
    16  // +build linux
    17  
    18  package sharedmem
    19  
    20  import (
    21  	"golang.org/x/sys/unix"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/buffer"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/cleanup"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/eventfd"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/link/sharedmem/pipe"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/tcpip/link/sharedmem/queue"
    28  )
    29  
    30  type serverRx struct {
    31  	// packetPipe represents the receive end of the pipe that carries the packet
    32  	// descriptors sent by the client.
    33  	packetPipe pipe.Rx
    34  
    35  	// completionPipe represents the transmit end of the pipe that will carry
    36  	// completion notifications from the server to the client.
    37  	completionPipe pipe.Tx
    38  
    39  	// data represents the buffer area where the packet payload is held.
    40  	data []byte
    41  
    42  	// eventFD is used to notify the peer when transmission is completed.
    43  	eventFD eventfd.Eventfd
    44  
    45  	// sharedData the memory region to use to enable/disable notifications.
    46  	sharedData []byte
    47  
    48  	// sharedEventFDState is the memory region in sharedData used to enable
    49  	// disable notifications on eventFD.
    50  	sharedEventFDState *atomicbitops.Uint32
    51  }
    52  
    53  // init initializes all state needed by the serverTx queue based on the
    54  // information provided.
    55  //
    56  // The caller always retains ownership of all file descriptors passed in. The
    57  // queue implementation will duplicate any that it may need in the future.
    58  func (s *serverRx) init(c *QueueConfig) error {
    59  	// Map in all buffers.
    60  	packetPipeMem, err := getBuffer(c.TxPipeFD)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	cu := cleanup.Make(func() { unix.Munmap(packetPipeMem) })
    65  	defer cu.Clean()
    66  
    67  	completionPipeMem, err := getBuffer(c.RxPipeFD)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	cu.Add(func() { unix.Munmap(completionPipeMem) })
    72  
    73  	data, err := getBuffer(c.DataFD)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	cu.Add(func() { unix.Munmap(data) })
    78  
    79  	sharedData, err := getBuffer(c.SharedDataFD)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	cu.Add(func() { unix.Munmap(sharedData) })
    84  
    85  	// Duplicate the eventFD so that caller can close it but we can still
    86  	// use it.
    87  	efd, err := c.EventFD.Dup()
    88  	if err != nil {
    89  		return err
    90  	}
    91  	cu.Add(func() { efd.Close() })
    92  
    93  	s.packetPipe.Init(packetPipeMem)
    94  	s.completionPipe.Init(completionPipeMem)
    95  	s.data = data
    96  	s.eventFD = efd
    97  	s.sharedData = sharedData
    98  	s.sharedEventFDState = sharedDataPointer(sharedData)
    99  
   100  	cu.Release()
   101  	return nil
   102  }
   103  
   104  func (s *serverRx) cleanup() {
   105  	unix.Munmap(s.packetPipe.Bytes())
   106  	unix.Munmap(s.completionPipe.Bytes())
   107  	unix.Munmap(s.data)
   108  	unix.Munmap(s.sharedData)
   109  	s.eventFD.Close()
   110  }
   111  
   112  // EnableNotification updates the shared state such that the peer will notify
   113  // the eventfd when there are packets to be dequeued.
   114  func (s *serverRx) EnableNotification() {
   115  	s.sharedEventFDState.Store(queue.EventFDEnabled)
   116  }
   117  
   118  // DisableNotification updates the shared state such that the peer will not
   119  // notify the eventfd.
   120  func (s *serverRx) DisableNotification() {
   121  	s.sharedEventFDState.Store(queue.EventFDDisabled)
   122  }
   123  
   124  // completionNotificationSize is size in bytes of a completion notification sent
   125  // on the completion queue after a transmitted packet has been handled.
   126  const completionNotificationSize = 8
   127  
   128  // receive receives a single packet from the packetPipe.
   129  func (s *serverRx) receive() *buffer.View {
   130  	desc := s.packetPipe.Pull()
   131  	if desc == nil {
   132  		return nil
   133  	}
   134  
   135  	pktInfo := queue.DecodeTxPacketHeader(desc)
   136  	contents := buffer.NewView(int(pktInfo.Size))
   137  	toCopy := pktInfo.Size
   138  	for i := 0; i < pktInfo.BufferCount; i++ {
   139  		txBuf := queue.DecodeTxBufferHeader(desc, i)
   140  		if txBuf.Size <= toCopy {
   141  			contents.Write(s.data[txBuf.Offset:][:txBuf.Size])
   142  			toCopy -= txBuf.Size
   143  			continue
   144  		}
   145  		contents.Write(s.data[txBuf.Offset:][:toCopy])
   146  		break
   147  	}
   148  
   149  	// Flush to let peer know that slots queued for transmission have been handled
   150  	// and its free to reuse the slots.
   151  	s.packetPipe.Flush()
   152  	// Encode packet completion.
   153  	b := s.completionPipe.Push(completionNotificationSize)
   154  	queue.EncodeTxCompletion(b, pktInfo.ID)
   155  	s.completionPipe.Flush()
   156  	return contents
   157  }
   158  
   159  func (s *serverRx) waitForPackets() {
   160  	s.eventFD.Wait()
   161  }