mirror of
https://github.com/v2fly/v2ray-core.git
synced 2026-04-08 14:55:28 -04:00
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
package gvisorstack
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
|
|
|
"github.com/v2fly/v2ray-core/v5/common/packetswitch"
|
|
)
|
|
|
|
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
|
|
|
|
type WrappedStack struct {
|
|
config *Config
|
|
ctx context.Context
|
|
stack *stack.Stack
|
|
|
|
stackTCPListeners []*gonet.TCPListener
|
|
}
|
|
|
|
func NewStack(ctx context.Context, config *Config) (*WrappedStack, error) {
|
|
return &WrappedStack{
|
|
config: config,
|
|
ctx: ctx,
|
|
}, nil
|
|
}
|
|
|
|
func (w *WrappedStack) CreateStackFromNetworkLayerDevice(packetSwitchDevice packetswitch.NetworkLayerDevice) error {
|
|
// Validate
|
|
if w == nil || w.config == nil {
|
|
return fmt.Errorf("no config")
|
|
}
|
|
|
|
// Determine MTU from config (0 means unspecified)
|
|
mtu := int(w.config.GetMtu())
|
|
|
|
// Create adaptor that implements stack.LinkEndpoint
|
|
adaptor := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(w.ctx, mtu, packetSwitchDevice)
|
|
|
|
// Create stack using adaptor as link endpoint
|
|
s, err := w.createStack(adaptor)
|
|
if err != nil {
|
|
// cleanup adaptor on error
|
|
adaptor.Close()
|
|
return fmt.Errorf("failed to create gvisor stack: %v", err)
|
|
}
|
|
|
|
// When the adaptor is closed, close the stack as well.
|
|
adaptor.SetOnCloseAction(func() {
|
|
if s != nil {
|
|
s.Close()
|
|
}
|
|
})
|
|
|
|
w.stack = s
|
|
|
|
if err := w.ApplyListeners(); err != nil {
|
|
return fmt.Errorf("failed to apply listeners: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *WrappedStack) createStack(linkedEndpoint stack.LinkEndpoint) (*stack.Stack, error) {
|
|
// Machine Generated
|
|
if w == nil || w.config == nil {
|
|
return nil, fmt.Errorf("no config")
|
|
}
|
|
|
|
s := stack.New(stack.Options{
|
|
NetworkProtocols: []stack.NetworkProtocolFactory{
|
|
ipv4.NewProtocol,
|
|
ipv6.NewProtocol,
|
|
},
|
|
TransportProtocols: []stack.TransportProtocolFactory{
|
|
tcp.NewProtocol,
|
|
udp.NewProtocol,
|
|
icmp.NewProtocol4,
|
|
icmp.NewProtocol6,
|
|
},
|
|
})
|
|
|
|
nicID := s.NextNICID()
|
|
|
|
// Create NIC
|
|
if err := s.CreateNICWithOptions(nicID, linkedEndpoint, stack.NICOptions{Disabled: false, QDisc: nil}); err != nil {
|
|
return nil, fmt.Errorf("failed to create NIC: %v", err)
|
|
}
|
|
|
|
// Add protocol addresses
|
|
for _, ip := range w.config.Ips {
|
|
tcpIPAddr := tcpip.AddrFromSlice(ip.Ip)
|
|
protocolAddress := tcpip.ProtocolAddress{
|
|
AddressWithPrefix: tcpip.AddressWithPrefix{
|
|
Address: tcpIPAddr,
|
|
PrefixLen: int(ip.Prefix),
|
|
},
|
|
}
|
|
|
|
switch tcpIPAddr.Len() {
|
|
case 4:
|
|
protocolAddress.Protocol = ipv4.ProtocolNumber
|
|
case 16:
|
|
protocolAddress.Protocol = ipv6.ProtocolNumber
|
|
default:
|
|
return nil, fmt.Errorf("invalid IP address length: %d", tcpIPAddr.Len())
|
|
}
|
|
|
|
if err := s.AddProtocolAddress(nicID, protocolAddress, stack.AddressProperties{}); err != nil {
|
|
return nil, fmt.Errorf("failed to add protocol address: %v", err)
|
|
}
|
|
}
|
|
|
|
// Set route table
|
|
s.SetRouteTable(func() (table []tcpip.Route) {
|
|
for _, cidrs := range w.config.Routes {
|
|
subnet := tcpip.AddressWithPrefix{
|
|
Address: tcpip.AddrFromSlice(cidrs.Ip),
|
|
PrefixLen: int(cidrs.Prefix),
|
|
}.Subnet()
|
|
route := tcpip.Route{
|
|
Destination: subnet,
|
|
NIC: nicID,
|
|
}
|
|
table = append(table, route)
|
|
}
|
|
return
|
|
}())
|
|
|
|
// Promiscuous & spoofing
|
|
if err := s.SetPromiscuousMode(nicID, w.config.EnablePromiscuousMode); err != nil {
|
|
return nil, fmt.Errorf("failed to set promiscuous mode: %v", err)
|
|
}
|
|
if err := s.SetSpoofing(nicID, w.config.EnableSpoofing); err != nil {
|
|
return nil, fmt.Errorf("failed to set spoofing: %v", err)
|
|
}
|
|
|
|
// Apply socket buffer sizes if provided
|
|
if w.config.SocketSettings != nil {
|
|
if size := w.config.SocketSettings.TxBufSize; size != 0 {
|
|
sendBufferSizeRangeOption := tcpip.TCPSendBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: int(size), Max: tcp.MaxBufferSize}
|
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sendBufferSizeRangeOption); err != nil {
|
|
return nil, fmt.Errorf("failed to set tcp send buffer size: %v", err)
|
|
}
|
|
}
|
|
|
|
if size := w.config.SocketSettings.RxBufSize; size != 0 {
|
|
receiveBufferSizeRangeOption := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: int(size), Max: tcp.MaxBufferSize}
|
|
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &receiveBufferSizeRangeOption); err != nil {
|
|
return nil, fmt.Errorf("failed to set tcp receive buffer size: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (w *WrappedStack) Close() error {
|
|
if w == nil || w.stack == nil {
|
|
return nil
|
|
}
|
|
for _, l := range w.stackTCPListeners {
|
|
l.Close()
|
|
}
|
|
w.stackTCPListeners = nil
|
|
w.stack.Close()
|
|
w.stack = nil
|
|
return nil
|
|
}
|