Feature: mark on socket (#1705)
This commit is contained in:
parent
e622d8dd38
commit
bd2ea2b917
11 changed files with 119 additions and 22 deletions
|
@ -16,6 +16,7 @@ type Base struct {
|
||||||
iface string
|
iface string
|
||||||
tp C.AdapterType
|
tp C.AdapterType
|
||||||
udp bool
|
udp bool
|
||||||
|
rmark int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements C.ProxyAdapter
|
// Name implements C.ProxyAdapter
|
||||||
|
@ -66,19 +67,25 @@ func (b *Base) DialOptions(opts ...dialer.Option) []dialer.Option {
|
||||||
opts = append(opts, dialer.WithInterface(b.iface))
|
opts = append(opts, dialer.WithInterface(b.iface))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.rmark != 0 {
|
||||||
|
opts = append(opts, dialer.WithRoutingMark(b.rmark))
|
||||||
|
}
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasicOption struct {
|
type BasicOption struct {
|
||||||
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
|
||||||
|
RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseOption struct {
|
type BaseOption struct {
|
||||||
Name string
|
Name string
|
||||||
Addr string
|
Addr string
|
||||||
Type C.AdapterType
|
Type C.AdapterType
|
||||||
UDP bool
|
UDP bool
|
||||||
Interface string
|
Interface string
|
||||||
|
RoutingMark int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBase(opt BaseOption) *Base {
|
func NewBase(opt BaseOption) *Base {
|
||||||
|
@ -88,6 +95,7 @@ func NewBase(opt BaseOption) *Base {
|
||||||
tp: opt.Type,
|
tp: opt.Type,
|
||||||
udp: opt.UDP,
|
udp: opt.UDP,
|
||||||
iface: opt.Interface,
|
iface: opt.Interface,
|
||||||
|
rmark: opt.RoutingMark,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Socks5 struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Socks5Option struct {
|
type Socks5Option struct {
|
||||||
*BaseOption
|
BasicOption
|
||||||
Name string `proxy:"name"`
|
Name string `proxy:"name"`
|
||||||
Server string `proxy:"server"`
|
Server string `proxy:"server"`
|
||||||
Port int `proxy:"port"`
|
Port int `proxy:"port"`
|
||||||
|
|
|
@ -94,9 +94,10 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
|
||||||
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
|
||||||
return &Fallback{
|
return &Fallback{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
Type: C.Fallback,
|
Type: C.Fallback,
|
||||||
Interface: option.Interface,
|
Interface: option.Interface,
|
||||||
|
RoutingMark: option.RoutingMark,
|
||||||
}),
|
}),
|
||||||
single: singledo.NewSingle(defaultGetProxiesDuration),
|
single: singledo.NewSingle(defaultGetProxiesDuration),
|
||||||
providers: providers,
|
providers: providers,
|
||||||
|
|
|
@ -171,9 +171,10 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
|
||||||
}
|
}
|
||||||
return &LoadBalance{
|
return &LoadBalance{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
Type: C.LoadBalance,
|
Type: C.LoadBalance,
|
||||||
Interface: option.Interface,
|
Interface: option.Interface,
|
||||||
|
RoutingMark: option.RoutingMark,
|
||||||
}),
|
}),
|
||||||
single: singledo.NewSingle(defaultGetProxiesDuration),
|
single: singledo.NewSingle(defaultGetProxiesDuration),
|
||||||
providers: providers,
|
providers: providers,
|
||||||
|
|
|
@ -103,9 +103,10 @@ func (r *Relay) proxies(metadata *C.Metadata, touch bool) []C.Proxy {
|
||||||
func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
|
func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
|
||||||
return &Relay{
|
return &Relay{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
Type: C.Relay,
|
Type: C.Relay,
|
||||||
Interface: option.Interface,
|
Interface: option.Interface,
|
||||||
|
RoutingMark: option.RoutingMark,
|
||||||
}),
|
}),
|
||||||
single: singledo.NewSingle(defaultGetProxiesDuration),
|
single: singledo.NewSingle(defaultGetProxiesDuration),
|
||||||
providers: providers,
|
providers: providers,
|
||||||
|
|
|
@ -101,9 +101,10 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider)
|
||||||
selected := providers[0].Proxies()[0].Name()
|
selected := providers[0].Proxies()[0].Name()
|
||||||
return &Selector{
|
return &Selector{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
Type: C.Selector,
|
Type: C.Selector,
|
||||||
Interface: option.Interface,
|
Interface: option.Interface,
|
||||||
|
RoutingMark: option.RoutingMark,
|
||||||
}),
|
}),
|
||||||
single: singledo.NewSingle(defaultGetProxiesDuration),
|
single: singledo.NewSingle(defaultGetProxiesDuration),
|
||||||
providers: providers,
|
providers: providers,
|
||||||
|
|
|
@ -137,9 +137,10 @@ func parseURLTestOption(config map[string]interface{}) []urlTestOption {
|
||||||
func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, options ...urlTestOption) *URLTest {
|
func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, options ...urlTestOption) *URLTest {
|
||||||
urlTest := &URLTest{
|
urlTest := &URLTest{
|
||||||
Base: outbound.NewBase(outbound.BaseOption{
|
Base: outbound.NewBase(outbound.BaseOption{
|
||||||
Name: option.Name,
|
Name: option.Name,
|
||||||
Type: C.URLTest,
|
Type: C.URLTest,
|
||||||
Interface: option.Interface,
|
Interface: option.Interface,
|
||||||
|
RoutingMark: option.RoutingMark,
|
||||||
}),
|
}),
|
||||||
single: singledo.NewSingle(defaultGetProxiesDuration),
|
single: singledo.NewSingle(defaultGetProxiesDuration),
|
||||||
fastSingle: singledo.NewSingle(time.Second * 10),
|
fastSingle: singledo.NewSingle(time.Second * 10),
|
||||||
|
|
|
@ -59,6 +59,9 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio
|
||||||
if cfg.addrReuse {
|
if cfg.addrReuse {
|
||||||
addrReuseToListenConfig(lc)
|
addrReuseToListenConfig(lc)
|
||||||
}
|
}
|
||||||
|
if cfg.routingMark != 0 {
|
||||||
|
bindMarkToListenConfig(cfg.routingMark, lc, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
return lc.ListenPacket(ctx, 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
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if opt.routingMark != 0 {
|
||||||
|
bindMarkToDialer(opt.routingMark, dialer, network, destination)
|
||||||
|
}
|
||||||
|
|
||||||
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port))
|
||||||
}
|
}
|
||||||
|
|
44
component/dialer/mark_linux.go
Normal file
44
component/dialer/mark_linux.go
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
27
component/dialer/mark_nonlinux.go
Normal file
27
component/dialer/mark_nonlinux.go
Normal 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()
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ var (
|
||||||
type option struct {
|
type option struct {
|
||||||
interfaceName string
|
interfaceName string
|
||||||
addrReuse bool
|
addrReuse bool
|
||||||
|
routingMark int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(opt *option)
|
type Option func(opt *option)
|
||||||
|
@ -25,3 +26,9 @@ func WithAddrReuse(reuse bool) Option {
|
||||||
opt.addrReuse = reuse
|
opt.addrReuse = reuse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithRoutingMark(mark int) Option {
|
||||||
|
return func(opt *option) {
|
||||||
|
opt.routingMark = mark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue