Browse Source

Feature: add support for multicast (#245)

* add support for multicast (#243)

* adjust setup

---------

Co-authored-by: xjasonlyu <xjasonlyu@gmail.com>
pull/288/head
Amaindex 1 year ago
committed by GitHub
parent
commit
90f77548ed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      core/nic.go
  2. 9
      core/stack.go
  3. 6
      engine/engine.go
  4. 1
      engine/key.go
  5. 18
      engine/parse.go
  6. 1
      main.go

61
core/nic.go

@ -2,8 +2,11 @@ package core
import (
"fmt"
"net"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"github.com/xjasonlyu/tun2socks/v2/core/option"
@ -56,3 +59,61 @@ func withSpoofing(nicID tcpip.NICID, v bool) option.Option {
return nil
}
}
// withMulticastGroups adds a NIC to the given multicast groups.
func withMulticastGroups(nicID tcpip.NICID, multicastGroups []net.IP) option.Option {
return func(s *stack.Stack) error {
if len(multicastGroups) == 0 {
return nil
}
// The default NIC of tun2socks is working on Spoofing mode. When the UDP Endpoint
// tries to use a non-local address to connect, the network stack will
// generate a temporary addressState to build the route, which can be primary
// but is ephemeral. Nevertheless, when the UDP Endpoint tries to use a
// multicast address to connect, the network stack will select an available
// primary addressState to build the route. However, when tun2socks is in the
// just-initialized or idle state, there will be no available primary addressState,
// and the connect operation will fail. Therefore, we need to add permanent addresses,
// e.g. 10.0.0.1/8 and fd00:1/8, to the default NIC, which are only used to build
// routes for multicast response and do not affect other connections.
//
// In fact, for multicast, the sender normally does not expect a response.
// So, the ep.net.Connect is unnecessary. If we implement a custom UDP Forwarder
// and ForwarderRequest in the future, we can remove these code.
s.AddProtocolAddress(
nicID,
tcpip.ProtocolAddress{
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tcpip.AddrFrom4([4]byte{0x0a, 0, 0, 0x01}),
PrefixLen: 8,
},
},
stack.AddressProperties{PEB: stack.CanBePrimaryEndpoint},
)
s.AddProtocolAddress(
nicID,
tcpip.ProtocolAddress{
Protocol: ipv6.ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tcpip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}),
PrefixLen: 8,
},
},
stack.AddressProperties{PEB: stack.CanBePrimaryEndpoint},
)
for _, multicastGroup := range multicastGroups {
if ip := multicastGroup.To4(); ip != nil {
if err := s.JoinGroup(ipv4.ProtocolNumber, nicID, tcpip.AddrFrom4Slice(ip)); err != nil {
return fmt.Errorf("join multicast group: %s", err)
}
} else {
ip := multicastGroup.To16()
if err := s.JoinGroup(ipv6.ProtocolNumber, nicID, tcpip.AddrFrom16Slice(ip)); err != nil {
return fmt.Errorf("join multicast group: %s", err)
}
}
}
return nil
}
}

9
core/stack.go

@ -1,6 +1,8 @@
package core
import (
"net"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
@ -23,6 +25,10 @@ type Config struct {
// stack to set transport handlers.
TransportHandler adapter.TransportHandler
// MulticastGroups is used by internal stack to add
// nic to given groups.
MulticastGroups []net.IP
// Options are supplement options to apply settings
// for the internal stack.
Options []option.Option
@ -88,6 +94,9 @@ func CreateStack(cfg *Config) (*stack.Stack, error) {
// Add default route table for IPv4 and IPv6. This will handle
// all incoming ICMP packets.
withRouteTable(nicID),
// Add default NIC to the given multicast groups.
withMulticastGroups(nicID, cfg.MulticastGroups),
)
for _, opt := range opts {

6
engine/engine.go

@ -193,6 +193,11 @@ func netstack(k *Key) (err error) {
return
}
var multicastGroups []net.IP
if multicastGroups, err = parseMulticastGroups(k.MulticastGroups); err != nil {
return err
}
var opts []option.Option
if k.TCPModerateReceiveBuffer {
opts = append(opts, option.WithTCPModerateReceiveBuffer(true))
@ -217,6 +222,7 @@ func netstack(k *Key) (err error) {
if _defaultStack, err = core.CreateStack(&core.Config{
LinkEndpoint: _defaultDevice,
TransportHandler: &mirror.Tunnel{},
MulticastGroups: multicastGroups,
Options: opts,
}); err != nil {
return

1
engine/key.go

@ -13,6 +13,7 @@ type Key struct {
TCPModerateReceiveBuffer bool `yaml:"tcp-moderate-receive-buffer"`
TCPSendBufferSize string `yaml:"tcp-send-buffer-size"`
TCPReceiveBufferSize string `yaml:"tcp-receive-buffer-size"`
MulticastGroups string `yaml:"multicast-groups"`
TUNPreUp string `yaml:"tun-pre-up"`
TUNPostUp string `yaml:"tun-post-up"`
UDPTimeout time.Duration `yaml:"udp-timeout"`

18
engine/parse.go

@ -150,3 +150,21 @@ func parseShadowsocks(u *url.URL) (address, method, password, obfsMode, obfsHost
return
}
func parseMulticastGroups(s string) (multicastGroups []net.IP, _ error) {
ipStrings := strings.Split(s, ",")
for _, ipString := range ipStrings {
if strings.TrimSpace(ipString) == "" {
continue
}
ip := net.ParseIP(ipString)
if ip == nil {
return nil, fmt.Errorf("invalid IP format: %s", ipString)
}
if !ip.IsMulticast() {
return nil, fmt.Errorf("invalid multicast IP address: %s", ipString)
}
multicastGroups = append(multicastGroups, ip)
}
return
}

1
main.go

@ -36,6 +36,7 @@ func init() {
flag.StringVar(&key.TCPSendBufferSize, "tcp-sndbuf", "", "Set TCP send buffer size for netstack")
flag.StringVar(&key.TCPReceiveBufferSize, "tcp-rcvbuf", "", "Set TCP receive buffer size for netstack")
flag.BoolVar(&key.TCPModerateReceiveBuffer, "tcp-auto-tuning", false, "Enable TCP receive buffer auto-tuning")
flag.StringVar(&key.MulticastGroups, "multicast-groups", "", "Set multicast groups, separated by commas")
flag.StringVar(&key.TUNPreUp, "tun-pre-up", "", "Execute a command before TUN device setup")
flag.StringVar(&key.TUNPostUp, "tun-post-up", "", "Execute a command after TUN device setup")
flag.BoolVar(&versionFlag, "version", false, "Show version and then quit")

Loading…
Cancel
Save