Feature: mark on socket (#1705)

This commit is contained in:
bobo liu 2021-11-08 16:59:48 +08:00 committed by GitHub
parent e622d8dd38
commit bd2ea2b917
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 119 additions and 22 deletions

View file

@ -16,6 +16,7 @@ type Base struct {
iface string
tp C.AdapterType
udp bool
rmark int
}
// Name implements C.ProxyAdapter
@ -66,11 +67,16 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
opts = append(opts, dialer.WithInterface(b.iface))
}
if b.rmark != 0 {
opts = append(opts, dialer.WithRoutingMark(b.rmark))
}
return opts
}
type BasicOption struct {
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
}
type BaseOption struct {
@ -79,6 +85,7 @@ type BaseOption struct {
Type C.AdapterType
UDP bool
Interface string
RoutingMark int
}
func NewBase(opt BaseOption) *Base {
@ -88,6 +95,7 @@ func NewBase(opt BaseOption) *Base {
tp: opt.Type,
udp: opt.UDP,
iface: opt.Interface,
rmark: opt.RoutingMark,
}
}

View file

@ -24,7 +24,7 @@ type Socks5 struct {
}
type Socks5Option struct {
*BaseOption
BasicOption
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`

View file

@ -97,6 +97,7 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
Name: option.Name,
Type: C.Fallback,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
}),
single: singledo.NewSingle(defaultGetProxiesDuration),
providers: providers,

View file

@ -174,6 +174,7 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
Name: option.Name,
Type: C.LoadBalance,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
}),
single: singledo.NewSingle(defaultGetProxiesDuration),
providers: providers,

View file

@ -106,6 +106,7 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re
Name: option.Name,
Type: C.Relay,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
}),
single: singledo.NewSingle(defaultGetProxiesDuration),
providers: providers,

View file

@ -104,6 +104,7 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider)
Name: option.Name,
Type: C.Selector,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
}),
single: singledo.NewSingle(defaultGetProxiesDuration),
providers: providers,

View file

@ -140,6 +140,7 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
Name: option.Name,
Type: C.URLTest,
Interface: option.Interface,
RoutingMark: option.RoutingMark,
}),
single: singledo.NewSingle(defaultGetProxiesDuration),
fastSingle: singledo.NewSingle(time.Second * 10),

View file

@ -59,6 +59,9 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
if cfg.addrReuse {
addrReuseToListenConfig(lc)
}
if cfg.routingMark != 0 {
bindMarkToListenConfig(cfg.routingMark, lc, network, address)
}
return lc.ListenPacket(ctx, network, address)
}
@ -82,6 +85,9 @@ func dialContext(ctx context.Context, network string, destination net.IP, port s
return nil, err
}
}
if opt.routingMark != 0 {
bindMarkToDialer(opt.routingMark, dialer, network, destination)
}
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
}

View file

@ -0,0 +1,44 @@
//go:build linux
// +build linux
package dialer
import (
"net"
"syscall"
)
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ net.IP) {
dialer.Control = bindMarkToControl(mark, dialer.Control)
}
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
lc.Control = bindMarkToControl(mark, lc.Control)
}
func bindMarkToControl(mark int, chain controlFn) controlFn {
return func(network, address string, c syscall.RawConn) (err error) {
defer func() {
if err == nil && chain != nil {
err = chain(network, address, c)
}
}()
ipStr, _, err := net.SplitHostPort(address)
if err == nil {
ip := net.ParseIP(ipStr)
if ip != nil && !ip.IsGlobalUnicast() {
return
}
}
return c.Control(func(fd uintptr) {
switch network {
case "tcp4", "udp4":
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
case "tcp6", "udp6":
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
}
})
}
}

View file

@ -0,0 +1,27 @@
//go:build !linux
// +build !linux
package dialer
import (
"net"
"sync"
"github.com/Dreamacro/clash/log"
)
var printMarkWarnOnce sync.Once
func printMarkWarn() {
printMarkWarnOnce.Do(func() {
log.Warnln("Routing mark on socket is not supported on current platform")
})
}
func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ net.IP) {
printMarkWarn()
}
func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, address string) {
printMarkWarn()
}

View file

@ -10,6 +10,7 @@ var (
type option struct {
interfaceName string
addrReuse bool
routingMark int
}
type Option func(opt *option)
@ -25,3 +26,9 @@ func WithAddrReuse(reuse bool) Option {
opt.addrReuse = reuse
}
}
func WithRoutingMark(mark int) Option {
return func(opt *option) {
opt.routingMark = mark
}
}