github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/runsc/boot/portforward/portforward_netstack.go (about)

     1  // Copyright 2023 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 portforward
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"sync"
    22  
    23  	"github.com/metacubex/gvisor/pkg/context"
    24  	"github.com/metacubex/gvisor/pkg/tcpip"
    25  	"github.com/metacubex/gvisor/pkg/tcpip/network/ipv4"
    26  	"github.com/metacubex/gvisor/pkg/tcpip/stack"
    27  	"github.com/metacubex/gvisor/pkg/tcpip/transport/tcp"
    28  	"github.com/metacubex/gvisor/pkg/waiter"
    29  )
    30  
    31  // netstackConn allows reading and writing to a netstack endpoint.
    32  // netstackConn implements proxyConn.
    33  type netstackConn struct {
    34  	// ep is the tcpip.Endpoint on which to read and write.
    35  	ep tcpip.Endpoint
    36  	// port is the port on which to connect.
    37  	port uint16
    38  	// wq is the WaitQueue for this connection to wait on notifications.
    39  	wq *waiter.Queue
    40  	// once makes sure Close is called once.
    41  	once sync.Once
    42  }
    43  
    44  // NewNetstackConn creates a new port forwarding connection to the given
    45  // port in netstack mode.
    46  func NewNetstackConn(stack *stack.Stack, port uint16) (proxyConn, error) {
    47  	var wq waiter.Queue
    48  	ep, tcpErr := stack.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
    49  	if tcpErr != nil {
    50  		return nil, fmt.Errorf("creating endpoint: %v", tcpErr)
    51  	}
    52  	n := &netstackConn{
    53  		ep:   ep,
    54  		port: port,
    55  		wq:   &wq,
    56  	}
    57  	waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
    58  	n.wq.EventRegister(&waitEntry)
    59  	defer n.wq.EventUnregister(&waitEntry)
    60  
    61  	tcpErr = n.ep.Connect(tcpip.FullAddress{
    62  		Addr: tcpip.AddrFrom4([4]byte{0x7f, 0x00, 0x00, 0x01}), // 127.0.0.1
    63  		Port: n.port,
    64  	})
    65  	if _, ok := tcpErr.(*tcpip.ErrConnectStarted); ok {
    66  		<-notifyCh
    67  		tcpErr = n.ep.LastError()
    68  	}
    69  	if tcpErr != nil {
    70  		return nil, fmt.Errorf("connecting endpoint: %v", tcpErr)
    71  	}
    72  	return n, nil
    73  }
    74  
    75  // Name implements proxyConn.Name.
    76  func (n *netstackConn) Name() string {
    77  	return fmt.Sprintf("netstack:port:%d", n.port)
    78  }
    79  
    80  // bufWriter is used as an io.Writer to read from tcpip.Endpoint.
    81  type bufWriter struct {
    82  	buf    []byte
    83  	offset int64
    84  }
    85  
    86  // Write implements io.Writer.
    87  func (b *bufWriter) Write(buf []byte) (int, error) {
    88  	n := copy(b.buf[b.offset:], buf)
    89  	b.offset += int64(n)
    90  	return n, nil
    91  }
    92  
    93  // Read implements proxyConn.Read.
    94  func (n *netstackConn) Read(ctx context.Context, buf []byte, cancel <-chan struct{}) (int, error) {
    95  	var ch chan struct{}
    96  	var e waiter.Entry
    97  	b := &bufWriter{
    98  		buf: buf,
    99  	}
   100  	res, tcpErr := n.ep.Read(b, tcpip.ReadOptions{})
   101  	for _, ok := tcpErr.(*tcpip.ErrWouldBlock); ok && ctx.Err() == nil; _, ok = tcpErr.(*tcpip.ErrWouldBlock) {
   102  		if ch == nil {
   103  			e, ch = waiter.NewChannelEntry(waiter.ReadableEvents | waiter.EventIn | waiter.EventHUp | waiter.EventErr)
   104  			n.wq.EventRegister(&e)
   105  			defer n.wq.EventUnregister(&e)
   106  		}
   107  		select {
   108  		case <-ch:
   109  		case <-cancel:
   110  			return 0, io.EOF
   111  		case <-ctx.Done():
   112  			return 0, ctx.Err()
   113  		}
   114  		res, tcpErr = n.ep.Read(b, tcpip.ReadOptions{})
   115  	}
   116  	if tcpErr != nil {
   117  		return 0, io.EOF
   118  	}
   119  	return res.Total, nil
   120  }
   121  
   122  // Write implements proxyConn.Write.
   123  func (n *netstackConn) Write(ctx context.Context, buf []byte, cancel <-chan struct{}) (int, error) {
   124  	var ch chan struct{}
   125  	var e waiter.Entry
   126  	var b bytes.Reader
   127  	b.Reset(buf)
   128  	res, tcpErr := n.ep.Write(&b, tcpip.WriteOptions{Atomic: true})
   129  	for _, ok := tcpErr.(*tcpip.ErrWouldBlock); ok && ctx.Err() == nil; _, ok = tcpErr.(*tcpip.ErrWouldBlock) {
   130  		if ch == nil {
   131  			e, ch = waiter.NewChannelEntry(waiter.WritableEvents | waiter.EventIn | waiter.EventHUp | waiter.EventErr)
   132  			n.wq.EventRegister(&e)
   133  			defer n.wq.EventUnregister(&e)
   134  		}
   135  		select {
   136  		case <-ch:
   137  		case <-cancel:
   138  			return 0, io.EOF
   139  		case <-ctx.Done():
   140  			return 0, ctx.Err()
   141  		}
   142  		res, tcpErr = n.ep.Write(&b, tcpip.WriteOptions{Atomic: true})
   143  	}
   144  	if tcpErr != nil {
   145  		return 0, io.EOF
   146  	}
   147  	return int(res), nil
   148  }
   149  
   150  // Close implements proxyConn.Close.
   151  func (n *netstackConn) Close(_ context.Context) {
   152  	n.once.Do(func() { n.ep.Close() })
   153  }