Feature: mark on socket (#1705)
This commit is contained in:
@ -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))
|
||||
}
|
||||
|
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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user