diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go
index 15eedd1ae1..e9119415ec 100644
--- a/adapter/outbound/base.go
+++ b/adapter/outbound/base.go
@@ -16,6 +16,7 @@ type Base struct {
 	iface string
 	tp    C.AdapterType
 	udp   bool
+	rmark int
 }
 
 // Name implements C.ProxyAdapter
@@ -66,19 +67,25 @@ 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"`
+	Interface   string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"`
+	RoutingMark int    `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"`
 }
 
 type BaseOption struct {
-	Name      string
-	Addr      string
-	Type      C.AdapterType
-	UDP       bool
-	Interface string
+	Name        string
+	Addr        string
+	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,
 	}
 }
 
diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go
index 874da9f145..d81c76140e 100644
--- a/adapter/outbound/socks5.go
+++ b/adapter/outbound/socks5.go
@@ -24,7 +24,7 @@ type Socks5 struct {
 }
 
 type Socks5Option struct {
-	*BaseOption
+	BasicOption
 	Name           string `proxy:"name"`
 	Server         string `proxy:"server"`
 	Port           int    `proxy:"port"`
diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go
index 2f9f0887fe..b08af487fa 100644
--- a/adapter/outboundgroup/fallback.go
+++ b/adapter/outboundgroup/fallback.go
@@ -94,9 +94,10 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
 func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
 	return &Fallback{
 		Base: outbound.NewBase(outbound.BaseOption{
-			Name:      option.Name,
-			Type:      C.Fallback,
-			Interface: option.Interface,
+			Name:        option.Name,
+			Type:        C.Fallback,
+			Interface:   option.Interface,
+			RoutingMark: option.RoutingMark,
 		}),
 		single:     singledo.NewSingle(defaultGetProxiesDuration),
 		providers:  providers,
diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go
index df1c2ba49b..26c8052ad5 100644
--- a/adapter/outboundgroup/loadbalance.go
+++ b/adapter/outboundgroup/loadbalance.go
@@ -171,9 +171,10 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
 	}
 	return &LoadBalance{
 		Base: outbound.NewBase(outbound.BaseOption{
-			Name:      option.Name,
-			Type:      C.LoadBalance,
-			Interface: option.Interface,
+			Name:        option.Name,
+			Type:        C.LoadBalance,
+			Interface:   option.Interface,
+			RoutingMark: option.RoutingMark,
 		}),
 		single:     singledo.NewSingle(defaultGetProxiesDuration),
 		providers:  providers,
diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go
index 4ec4ffacfd..393a69bb92 100644
--- a/adapter/outboundgroup/relay.go
+++ b/adapter/outboundgroup/relay.go
@@ -103,9 +103,10 @@ func (r *Relay) proxies(metadata *C.Metadata, touch bool) []C.Proxy {
 func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay {
 	return &Relay{
 		Base: outbound.NewBase(outbound.BaseOption{
-			Name:      option.Name,
-			Type:      C.Relay,
-			Interface: option.Interface,
+			Name:        option.Name,
+			Type:        C.Relay,
+			Interface:   option.Interface,
+			RoutingMark: option.RoutingMark,
 		}),
 		single:    singledo.NewSingle(defaultGetProxiesDuration),
 		providers: providers,
diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go
index 4ec67cd1d9..47e4b87d08 100644
--- a/adapter/outboundgroup/selector.go
+++ b/adapter/outboundgroup/selector.go
@@ -101,9 +101,10 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider)
 	selected := providers[0].Proxies()[0].Name()
 	return &Selector{
 		Base: outbound.NewBase(outbound.BaseOption{
-			Name:      option.Name,
-			Type:      C.Selector,
-			Interface: option.Interface,
+			Name:        option.Name,
+			Type:        C.Selector,
+			Interface:   option.Interface,
+			RoutingMark: option.RoutingMark,
 		}),
 		single:     singledo.NewSingle(defaultGetProxiesDuration),
 		providers:  providers,
diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go
index 6866db0df9..47144c0468 100644
--- a/adapter/outboundgroup/urltest.go
+++ b/adapter/outboundgroup/urltest.go
@@ -137,9 +137,10 @@ func parseURLTestOption(config map[string]interface{}) []urlTestOption {
 func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, options ...urlTestOption) *URLTest {
 	urlTest := &URLTest{
 		Base: outbound.NewBase(outbound.BaseOption{
-			Name:      option.Name,
-			Type:      C.URLTest,
-			Interface: option.Interface,
+			Name:        option.Name,
+			Type:        C.URLTest,
+			Interface:   option.Interface,
+			RoutingMark: option.RoutingMark,
 		}),
 		single:     singledo.NewSingle(defaultGetProxiesDuration),
 		fastSingle: singledo.NewSingle(time.Second * 10),
diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go
index 87b62dd65a..c84fcaa463 100644
--- a/component/dialer/dialer.go
+++ b/component/dialer/dialer.go
@@ -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))
 }
diff --git a/component/dialer/mark_linux.go b/component/dialer/mark_linux.go
new file mode 100644
index 0000000000..79a2185e40
--- /dev/null
+++ b/component/dialer/mark_linux.go
@@ -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)
+			}
+		})
+	}
+}
diff --git a/component/dialer/mark_nonlinux.go b/component/dialer/mark_nonlinux.go
new file mode 100644
index 0000000000..5d9befb1a0
--- /dev/null
+++ b/component/dialer/mark_nonlinux.go
@@ -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()
+}
diff --git a/component/dialer/options.go b/component/dialer/options.go
index 96d9eb751f..b3cca81051 100644
--- a/component/dialer/options.go
+++ b/component/dialer/options.go
@@ -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
+	}
+}