Преглед изворни кода

Add control options for listeners

世界 пре 5 месеци
родитељ
комит
f0f1942f1f

+ 11 - 0
common/listener/listener_tcp.go

@@ -8,9 +8,11 @@ import (
 	"github.com/sagernet/sing-box/adapter"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
+	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/service"
 
 	"github.com/metacubex/tfo-go"
 )
@@ -23,6 +25,15 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
 	var err error
 	bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
 	var listenConfig net.ListenConfig
+	if l.listenOptions.BindInterface != "" {
+		listenConfig.Control = control.Append(listenConfig.Control, control.BindToInterface(service.FromContext[adapter.NetworkManager](l.ctx).InterfaceFinder(), l.listenOptions.BindInterface, -1))
+	}
+	if l.listenOptions.RoutingMark != 0 {
+		listenConfig.Control = control.Append(listenConfig.Control, control.RoutingMark(uint32(l.listenOptions.RoutingMark)))
+	}
+	if l.listenOptions.ReuseAddr {
+		listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
+	}
 	if l.listenOptions.TCPKeepAlive >= 0 {
 		keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
 		if keepIdle == 0 {

+ 32 - 3
common/listener/listener_udp.go

@@ -6,16 +6,27 @@ import (
 	"net/netip"
 	"os"
 
+	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing/common/buf"
 	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/service"
 )
 
 func (l *Listener) ListenUDP() (net.PacketConn, error) {
 	bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
-	var lc net.ListenConfig
+	var listenConfig net.ListenConfig
+	if l.listenOptions.BindInterface != "" {
+		listenConfig.Control = control.Append(listenConfig.Control, control.BindToInterface(service.FromContext[adapter.NetworkManager](l.ctx).InterfaceFinder(), l.listenOptions.BindInterface, -1))
+	}
+	if l.listenOptions.RoutingMark != 0 {
+		listenConfig.Control = control.Append(listenConfig.Control, control.RoutingMark(uint32(l.listenOptions.RoutingMark)))
+	}
+	if l.listenOptions.ReuseAddr {
+		listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
+	}
 	var udpFragment bool
 	if l.listenOptions.UDPFragment != nil {
 		udpFragment = *l.listenOptions.UDPFragment
@@ -23,10 +34,10 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
 		udpFragment = l.listenOptions.UDPFragmentDefault
 	}
 	if !udpFragment {
-		lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
+		listenConfig.Control = control.Append(listenConfig.Control, control.DisableUDPFragment())
 	}
 	udpConn, err := ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
-		return lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
+		return listenConfig.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
 	})
 	if err != nil {
 		return nil, err
@@ -39,12 +50,30 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
 
 func (l *Listener) DialContext(dialer net.Dialer, ctx context.Context, network string, address string) (net.Conn, error) {
 	return ListenNetworkNamespace[net.Conn](l.listenOptions.NetNs, func() (net.Conn, error) {
+		if l.listenOptions.BindInterface != "" {
+			dialer.Control = control.Append(dialer.Control, control.BindToInterface(service.FromContext[adapter.NetworkManager](l.ctx).InterfaceFinder(), l.listenOptions.BindInterface, -1))
+		}
+		if l.listenOptions.RoutingMark != 0 {
+			dialer.Control = control.Append(dialer.Control, control.RoutingMark(uint32(l.listenOptions.RoutingMark)))
+		}
+		if l.listenOptions.ReuseAddr {
+			dialer.Control = control.Append(dialer.Control, control.ReuseAddr())
+		}
 		return dialer.DialContext(ctx, network, address)
 	})
 }
 
 func (l *Listener) ListenPacket(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.PacketConn, error) {
 	return ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
+		if l.listenOptions.BindInterface != "" {
+			listenConfig.Control = control.Append(listenConfig.Control, control.BindToInterface(service.FromContext[adapter.NetworkManager](l.ctx).InterfaceFinder(), l.listenOptions.BindInterface, -1))
+		}
+		if l.listenOptions.RoutingMark != 0 {
+			listenConfig.Control = control.Append(listenConfig.Control, control.RoutingMark(uint32(l.listenOptions.RoutingMark)))
+		}
+		if l.listenOptions.ReuseAddr {
+			listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
+		}
 		return listenConfig.ListenPacket(ctx, network, address)
 	})
 }

+ 15 - 11
docs/configuration/shared/dial.md

@@ -25,11 +25,12 @@ icon: material/new-box
   "inet6_bind_address": "",
   "routing_mark": 0,
   "reuse_addr": false,
+  "netns": "",
   "connect_timeout": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
-  "netns": "",
+  
   "domain_resolver": "", // or {}
   "network_strategy": "",
   "network_type": [],
@@ -37,6 +38,7 @@ icon: material/new-box
   "fallback_delay": "",
 
   // Deprecated
+  
   "domain_strategy": ""
 }
 ```
@@ -73,10 +75,22 @@ The IPv6 address to bind to.
 
 Set netfilter routing mark.
 
+Integers (e.g. `1234`) and string hexadecimals (e.g. `"0x1234"`) are supported.
+
 #### reuse_addr
 
 Reuse listener address.
 
+#### netns
+
+!!! question "Since sing-box 1.12.0"
+
+!!! quote ""
+
+    Only supported on Linux.
+
+Set network namespace, name or path.
+
 #### connect_timeout
 
 Connect timeout, in golang's Duration format.
@@ -102,16 +116,6 @@ Enable TCP Multi Path.
 
 Enable UDP fragmentation.
 
-#### netns
-
-!!! question "Since sing-box 1.12.0"
-
-!!! quote ""
-
-    Only supported on Linux.
-
-Set network namespace, name or path.
-
 #### domain_resolver
 
 !!! warning ""

+ 13 - 11
docs/configuration/shared/dial.zh.md

@@ -25,11 +25,11 @@ icon: material/new-box
   "inet6_bind_address": "",
   "routing_mark": 0,
   "reuse_addr": false,
+  "netns": "",
   "connect_timeout": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
-  "netns": "",
   "domain_resolver": "", // 或 {}
   "network_strategy": "",
   "network_type": [],
@@ -74,10 +74,22 @@ icon: material/new-box
 
 设置 netfilter 路由标记。
 
+支持数字 (如 `1234`) 和十六进制字符串 (如 `"0x1234"`)。
+
 #### reuse_addr
 
 重用监听地址。
 
+#### netns
+
+!!! question "自 sing-box 1.12.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+设置网络命名空间,名称或路径。
+
 #### connect_timeout
 
 连接超时,采用 golang 的 Duration 格式。
@@ -101,16 +113,6 @@ icon: material/new-box
 
 启用 UDP 分段。
 
-#### netns
-
-!!! question "自 sing-box 1.12.0 起"
-
-!!! quote ""
-
-    仅支持 Linux。
-
-设置网络命名空间,名称或路径。
-
 #### domain_resolver
 
 !!! warning ""

+ 45 - 21
docs/configuration/shared/listen.md

@@ -4,7 +4,10 @@ icon: material/new-box
 
 !!! quote "Changes in sing-box 1.12.0"
 
-    :material-plus: [netns](#netns)
+    :material-plus: [netns](#netns)  
+    :material-plus: [bind_interface](#bind_interface)  
+    :material-plus: [routing_mark](#routing_mark)  
+    :material-plus: [reuse_addr](#reuse_addr)
 
 !!! quote "Changes in sing-box 1.11.0"
 
@@ -20,12 +23,18 @@ icon: material/new-box
 {
   "listen": "",
   "listen_port": 0,
+  "bind_interface": "",
+  "routing_mark": 0,
+  "reuse_addr": false,
+  "netns": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
   "udp_timeout": "",
-  "netns": "",
   "detour": "",
+
+  // Deprecated
+  
   "sniff": false,
   "sniff_override_destination": false,
   "sniff_timeout": "",
@@ -36,15 +45,6 @@ icon: material/new-box
 
 ### Fields
 
-| Field                          | Available Context                                       |
-|--------------------------------|---------------------------------------------------------|
-| `listen`                       | Needs to listen on TCP or UDP.                          |
-| `listen_port`                  | Needs to listen on TCP or UDP.                          |
-| `tcp_fast_open`                | Needs to listen on TCP.                                 |
-| `tcp_multi_path`               | Needs to listen on TCP.                                 |
-| `udp_timeout`                  | Needs to assemble UDP connections.                      |
-| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. |
-
 #### listen
 
 ==Required==
@@ -55,6 +55,40 @@ Listen address.
 
 Listen port.
 
+#### bind_interface
+
+!!! question "Since sing-box 1.12.0"
+
+The network interface to bind to.
+
+#### routing_mark
+
+!!! question "Since sing-box 1.12.0"
+
+!!! quote ""
+
+    Only supported on Linux.
+
+Set netfilter routing mark.
+
+Integers (e.g. `1234`) and string hexadecimals (e.g. `"0x1234"`) are supported.
+
+#### reuse_addr
+
+!!! question "Since sing-box 1.12.0"
+
+Reuse listener address.
+
+#### netns
+
+!!! question "Since sing-box 1.12.0"
+
+!!! quote ""
+
+    Only supported on Linux.
+
+Set network namespace, name or path.
+
 #### tcp_fast_open
 
 Enable TCP Fast Open.
@@ -77,16 +111,6 @@ UDP NAT expiration time.
 
 `5m` will be used by default.
 
-#### netns
-
-!!! question "Since sing-box 1.12.0"
-
-!!! quote ""
-
-    Only supported on Linux.
-
-Set network namespace, name or path.
-
 #### detour
 
 If set, connections will be forwarded to the specified inbound.

+ 45 - 22
docs/configuration/shared/listen.zh.md

@@ -4,7 +4,10 @@ icon: material/new-box
 
 !!! quote "Changes in sing-box 1.12.0"
 
-    :material-plus: [netns](#netns)
+    :material-plus: [netns](#netns)  
+    :material-plus: [bind_interface](#bind_interface)  
+    :material-plus: [routing_mark](#routing_mark)  
+    :material-plus: [reuse_addr](#reuse_addr)
 
 !!! quote "sing-box 1.11.0 中的更改"
 
@@ -20,12 +23,18 @@ icon: material/new-box
 {
   "listen": "",
   "listen_port": 0,
+  "bind_interface": "",
+  "routing_mark": 0,
+  "reuse_addr": false,
+  "netns": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
   "udp_timeout": "",
-  "netns": "",
   "detour": "",
+
+  // 废弃的
+  
   "sniff": false,
   "sniff_override_destination": false,
   "sniff_timeout": "",
@@ -34,16 +43,6 @@ icon: material/new-box
 }
 ```
 
-
-| 字段               | 可用上下文           |
-|------------------|-----------------|
-| `listen`         | 需要监听 TCP 或 UDP。 |
-| `listen_port`    | 需要监听 TCP 或 UDP。 |
-| `tcp_fast_open`  | 需要监听 TCP。       |
-| `tcp_multi_path` | 需要监听 TCP。       |
-| `udp_timeout`    | 需要组装 UDP 连接。    |
-| 
-
 ### 字段
 
 #### listen
@@ -56,6 +55,40 @@ icon: material/new-box
 
 监听端口。
 
+#### bind_interface
+
+!!! question "自 sing-box 1.12.0 起"
+
+要绑定到的网络接口。
+
+#### routing_mark
+
+!!! question "自 sing-box 1.12.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+设置 netfilter 路由标记。
+
+支持数字 (如 `1234`) 和十六进制字符串 (如 `"0x1234"`)。
+
+#### reuse_addr
+
+!!! question "自 sing-box 1.12.0 起"
+
+重用监听地址。
+
+#### netns
+
+!!! question "自 sing-box 1.12.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+设置网络命名空间,名称或路径。
+
 #### tcp_fast_open
 
 启用 TCP Fast Open。
@@ -78,16 +111,6 @@ UDP NAT 过期时间。
 
 默认使用 `5m`。
 
-#### netns
-
-!!! question "自 sing-box 1.12.0 起"
-
-!!! quote ""
-
-    仅支持 Linux。
-
-设置网络命名空间,名称或路径。
-
 #### detour
 
 如果设置,连接将被转发到指定的入站。

+ 4 - 1
option/inbound.go

@@ -61,6 +61,10 @@ type InboundOptions struct {
 type ListenOptions struct {
 	Listen               *badoption.Addr    `json:"listen,omitempty"`
 	ListenPort           uint16             `json:"listen_port,omitempty"`
+	BindInterface        string             `json:"bind_interface,omitempty"`
+	RoutingMark          FwMark             `json:"routing_mark,omitempty"`
+	ReuseAddr            bool               `json:"reuse_addr,omitempty"`
+	NetNs                string             `json:"netns,omitempty"`
 	TCPKeepAlive         badoption.Duration `json:"tcp_keep_alive,omitempty"`
 	TCPKeepAliveInterval badoption.Duration `json:"tcp_keep_alive_interval,omitempty"`
 	TCPFastOpen          bool               `json:"tcp_fast_open,omitempty"`
@@ -68,7 +72,6 @@ type ListenOptions struct {
 	UDPFragment          *bool              `json:"udp_fragment,omitempty"`
 	UDPFragmentDefault   bool               `json:"-"`
 	UDPTimeout           UDPTimeoutCompat   `json:"udp_timeout,omitempty"`
-	NetNs                string             `json:"netns,omitempty"`
 
 	// Deprecated: removed
 	ProxyProtocol bool `json:"proxy_protocol,omitempty"`

+ 1 - 1
option/outbound.go

@@ -72,12 +72,12 @@ type DialerOptions struct {
 	ProtectPath         string                            `json:"protect_path,omitempty"`
 	RoutingMark         FwMark                            `json:"routing_mark,omitempty"`
 	ReuseAddr           bool                              `json:"reuse_addr,omitempty"`
+	NetNs               string                            `json:"netns,omitempty"`
 	ConnectTimeout      badoption.Duration                `json:"connect_timeout,omitempty"`
 	TCPFastOpen         bool                              `json:"tcp_fast_open,omitempty"`
 	TCPMultiPath        bool                              `json:"tcp_multi_path,omitempty"`
 	UDPFragment         *bool                             `json:"udp_fragment,omitempty"`
 	UDPFragmentDefault  bool                              `json:"-"`
-	NetNs               string                            `json:"netns,omitempty"`
 	DomainResolver      *DomainResolveOptions             `json:"domain_resolver,omitempty"`
 	NetworkStrategy     *NetworkStrategy                  `json:"network_strategy,omitempty"`
 	NetworkType         badoption.Listable[InterfaceType] `json:"network_type,omitempty"`