//go:build with_gvisor package openvpn import ( "context" "net" "net/netip" "slices" "sync" "sync/atomic" "github.com/sagernet/gvisor/pkg/buffer" "github.com/sagernet/gvisor/pkg/tcpip" "github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet" "github.com/sagernet/gvisor/pkg/tcpip/header" "github.com/sagernet/gvisor/pkg/tcpip/network/ipv4" "github.com/sagernet/gvisor/pkg/tcpip/network/ipv6" "github.com/sagernet/gvisor/pkg/tcpip/stack" "github.com/sagernet/gvisor/pkg/tcpip/transport/icmp" "github.com/sagernet/gvisor/pkg/tcpip/transport/tcp" "github.com/sagernet/gvisor/pkg/tcpip/transport/udp" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) var _ Device = (*stackDevice)(nil) type stackDevice struct { baseDevice stateAccess sync.RWMutex options DeviceOptions stack *stack.Stack endpoint *stackEndpoint inet4Address netip.Addr inet6Address netip.Addr udpForwarder *tun.UDPForwarder icmpForwarder *tun.ICMPForwarder logRouteOptions bool closeOnce sync.Once } func newStackDevice(options DeviceOptions) (*stackDevice, error) { if options.MTU == 0 { options.MTU = DefaultMTU } device := &stackDevice{ options: options, logRouteOptions: true, } endpoint := &stackEndpoint{ device: device, done: make(chan struct{}), } endpoint.mtu.Store(options.MTU) ipStack, err := tun.NewGVisorStackWithOptions(endpoint, stack.NICOptions{}, true) if err != nil { return nil, err } device.stack = ipStack device.endpoint = endpoint err = device.updateAddresses(nil, options.Configuration.Address) if err != nil { ipStack.Close() return nil, err } if options.Handler != nil { ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket) udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{ Timeout: options.UDPTimeout, Shared: true, Mapping: options.UDPMapping, Filtering: options.UDPFiltering, MaxSize: options.UDPNATMax, InterfaceFinder: options.InterfaceFinder, ExcludeInterface: options.ExcludeInterface, }) ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket) device.udpForwarder = udpForwarder icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger) ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket) ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket) device.icmpForwarder = icmpForwarder } return device, nil } func (d *stackDevice) Start() error { if d.udpForwarder != nil { err := d.udpForwarder.Start() if err != nil { return err } } return nil } func (d *stackDevice) UpdateConfiguration(configuration Configuration) error { d.stateAccess.Lock() defer d.stateAccess.Unlock() if d.logRouteOptions && hasRouteOptions(configuration.Routes) { d.options.Logger.Debug("route gateway and metric options are not representable by the gVisor stack device; routes are installed by prefix") d.logRouteOptions = false } if configuration.MTU != 0 { d.options.MTU = configuration.MTU d.endpoint.mtu.Store(configuration.MTU) } previousAddresses := d.options.Configuration.Address d.options.Configuration = configuration return d.updateAddresses(previousAddresses, configuration.Address) } func (d *stackDevice) updateAddresses(previousAddresses []netip.Prefix, addresses []netip.Prefix) error { for _, prefix := range previousAddresses { if slices.Contains(addresses, prefix) { continue } gErr := d.stack.RemoveAddress(tun.DefaultNIC, tun.AddressFromAddr(prefix.Addr())) if gErr != nil { return E.New("remove local address ", prefix, ": ", gErr.String()) } } for _, prefix := range addresses { if slices.Contains(previousAddresses, prefix) { continue } protocolAddress := tcpip.ProtocolAddress{ AddressWithPrefix: tcpip.AddressWithPrefix{ Address: tun.AddressFromAddr(prefix.Addr()), PrefixLen: prefix.Bits(), }, } if prefix.Addr().Is4() { protocolAddress.Protocol = ipv4.ProtocolNumber } else { protocolAddress.Protocol = ipv6.ProtocolNumber } gErr := d.stack.AddProtocolAddress(tun.DefaultNIC, protocolAddress, stack.AddressProperties{}) if gErr != nil { return E.New("add local address ", prefix, ": ", gErr.String()) } } d.inet4Address, d.inet6Address = firstAddresses(addresses) return nil } func (d *stackDevice) WriteInboundBuffers(packetBuffers []*buf.Buffer) error { return d.processInboundBuffers(packetBuffers, d.writeBuffers) } func (d *stackDevice) writeBuffers(packetBuffers []*buf.Buffer) error { networkProtocols := make([]tcpip.NetworkProtocolNumber, 0, len(packetBuffers)) stackPacketBuffers := make([]*stack.PacketBuffer, 0, len(packetBuffers)) var packetErr error for _, packetBuffer := range packetBuffers { packet := packetBuffer.Bytes() var networkProtocol tcpip.NetworkProtocolNumber switch header.IPVersion(packet) { case header.IPv4Version: networkProtocol = header.IPv4ProtocolNumber case header.IPv6Version: networkProtocol = header.IPv6ProtocolNumber default: if packetErr == nil { packetErr = E.New("invalid IP packet") } continue } networkProtocols = append(networkProtocols, networkProtocol) stackPacketBuffers = append(stackPacketBuffers, stack.NewPacketBuffer(stack.PacketBufferOptions{ Payload: buffer.MakeWithData(packet), })) } d.endpoint.deliverNetworkPackets(networkProtocols, stackPacketBuffers) for _, packetBuffer := range stackPacketBuffers { packetBuffer.DecRef() } return packetErr } func (d *stackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if destination.IsIPv6() && d.blockIPv6Enabled() { return nil, E.New("IPv6 blocked by pushed block-ipv6") } inet4Address, inet6Address := d.PortAddresses() address := tcpip.FullAddress{ NIC: tun.DefaultNIC, Port: destination.Port, Addr: tun.AddressFromAddr(destination.Addr), } bind := tcpip.FullAddress{ NIC: tun.DefaultNIC, } var networkProtocol tcpip.NetworkProtocolNumber if destination.IsIPv4() { if !inet4Address.IsValid() { return nil, E.New("missing IPv4 local address") } networkProtocol = header.IPv4ProtocolNumber bind.Addr = tun.AddressFromAddr(inet4Address) } else { if !inet6Address.IsValid() { return nil, E.New("missing IPv6 local address") } networkProtocol = header.IPv6ProtocolNumber bind.Addr = tun.AddressFromAddr(inet6Address) } switch N.NetworkName(network) { case N.NetworkTCP: return gonet.DialTCPWithBind(ctx, d.stack, bind, address, networkProtocol) case N.NetworkUDP: return gonet.DialUDP(d.stack, &bind, &address, networkProtocol) default: return nil, E.Extend(N.ErrUnknownNetwork, network) } } func (d *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if destination.IsIPv6() && d.blockIPv6Enabled() { return nil, E.New("IPv6 blocked by pushed block-ipv6") } inet4Address, inet6Address := d.PortAddresses() bind := tcpip.FullAddress{ NIC: tun.DefaultNIC, } var networkProtocol tcpip.NetworkProtocolNumber if destination.IsIPv4() { if !inet4Address.IsValid() { return nil, E.New("missing IPv4 local address") } networkProtocol = header.IPv4ProtocolNumber bind.Addr = tun.AddressFromAddr(inet4Address) } else { if !inet6Address.IsValid() { return nil, E.New("missing IPv6 local address") } networkProtocol = header.IPv6ProtocolNumber bind.Addr = tun.AddressFromAddr(inet6Address) } return gonet.DialUDP(d.stack, &bind, nil, networkProtocol) } func (d *stackDevice) blockIPv6Enabled() bool { d.stateAccess.RLock() defer d.stateAccess.RUnlock() return d.options.Configuration.BlockIPv6 } func (d *stackDevice) PortAddresses() (netip.Addr, netip.Addr) { d.stateAccess.RLock() defer d.stateAccess.RUnlock() return d.inet4Address, d.inet6Address } func (d *stackDevice) PortMTU() uint32 { d.stateAccess.RLock() defer d.stateAccess.RUnlock() return d.options.MTU } func (d *stackDevice) Close() error { d.closeOnce.Do(func() { close(d.endpoint.done) if d.udpForwarder != nil { d.udpForwarder.Close() } if d.icmpForwarder != nil { d.icmpForwarder.Close() } d.stack.Close() for _, endpoint := range d.stack.CleanupEndpoints() { endpoint.Abort() } d.stack.Wait() }) return nil } type stackEndpoint struct { device *stackDevice mtu atomic.Uint32 done chan struct{} dispatcherAccess sync.RWMutex dispatcher stack.NetworkDispatcher } func (e *stackEndpoint) MTU() uint32 { return e.mtu.Load() } func (e *stackEndpoint) SetMTU(mtu uint32) { e.mtu.Store(mtu) } func (e *stackEndpoint) MaxHeaderLength() uint16 { return 0 } func (e *stackEndpoint) LinkAddress() tcpip.LinkAddress { return "" } func (e *stackEndpoint) SetLinkAddress(addr tcpip.LinkAddress) { } func (e *stackEndpoint) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityRXChecksumOffload } func (e *stackEndpoint) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcherAccess.Lock() defer e.dispatcherAccess.Unlock() e.dispatcher = dispatcher } func (e *stackEndpoint) IsAttached() bool { e.dispatcherAccess.RLock() defer e.dispatcherAccess.RUnlock() return e.dispatcher != nil } func (e *stackEndpoint) deliverNetworkPackets(networkProtocols []tcpip.NetworkProtocolNumber, packetBuffers []*stack.PacketBuffer) { e.dispatcherAccess.RLock() defer e.dispatcherAccess.RUnlock() if e.dispatcher == nil { return } for i, packetBuffer := range packetBuffers { e.dispatcher.DeliverNetworkPacket(networkProtocols[i], packetBuffer) } } func (e *stackEndpoint) Wait() { } func (e *stackEndpoint) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareNone } func (e *stackEndpoint) AddHeader(packetBuffer *stack.PacketBuffer) { } func (e *stackEndpoint) ParseHeader(packetBuffer *stack.PacketBuffer) bool { return true } func (e *stackEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) { packetBuffers := make([]*buf.Buffer, 0, list.Len()) for _, packetBuffer := range list.AsSlice() { packetSlices := packetBuffer.AsSlices() packetLength := 0 for _, packetSlice := range packetSlices { packetLength += len(packetSlice) } outboundBuffer := buf.NewSize(PacketHeadroom + packetLength + systemDevicePacketRearSpace) outboundBuffer.Resize(PacketHeadroom, 0) for _, packetSlice := range packetSlices { _, _ = outboundBuffer.Write(packetSlice) } packetBuffers = append(packetBuffers, outboundBuffer) } err := e.device.writeOutbound(packetBuffers) if err != nil { return 0, &tcpip.ErrClosedForSend{} } return list.Len(), nil } func (e *stackEndpoint) Close() { } func (e *stackEndpoint) SetOnCloseAction(action func()) { }