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 }