mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-03-31 02:26:20 +00:00
Add netns support
This commit is contained in:
parent
786d17cb6a
commit
7669763137
9 changed files with 204 additions and 43 deletions
common
docs/configuration/shared
option
|
@ -5,6 +5,8 @@ import (
|
|||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
|
@ -20,6 +22,8 @@ import (
|
|||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,6 +39,7 @@ type DefaultDialer struct {
|
|||
udpListener net.ListenConfig
|
||||
udpAddr4 string
|
||||
udpAddr6 string
|
||||
netns string
|
||||
networkManager adapter.NetworkManager
|
||||
networkStrategy *C.NetworkStrategy
|
||||
defaultNetworkStrategy bool
|
||||
|
@ -198,6 +203,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||
udpListener: listener,
|
||||
udpAddr4: udpAddr4,
|
||||
udpAddr6: udpAddr6,
|
||||
netns: options.NetNs,
|
||||
networkManager: networkManager,
|
||||
networkStrategy: networkStrategy,
|
||||
defaultNetworkStrategy: defaultNetworkStrategy,
|
||||
|
@ -214,6 +220,29 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
|||
return nil, E.New("domain not resolved")
|
||||
}
|
||||
if d.networkStrategy == nil {
|
||||
if d.netns != "" {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
currentNs, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get current netns")
|
||||
}
|
||||
defer netns.Set(currentNs)
|
||||
var targetNs netns.NsHandle
|
||||
if strings.HasPrefix(d.netns, "/") {
|
||||
targetNs, err = netns.GetFromPath(d.netns)
|
||||
} else {
|
||||
targetNs, err = netns.GetFromName(d.netns)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get netns ", d.netns)
|
||||
}
|
||||
defer targetNs.Close()
|
||||
err = netns.Set(targetNs)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "set netns to ", d.netns)
|
||||
}
|
||||
}
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
if !address.IsIPv6() {
|
||||
|
@ -282,6 +311,29 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if d.networkStrategy == nil {
|
||||
if d.netns != "" {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
currentNs, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get current netns")
|
||||
}
|
||||
defer netns.Set(currentNs)
|
||||
var targetNs netns.NsHandle
|
||||
if strings.HasPrefix(d.netns, "/") {
|
||||
targetNs, err = netns.GetFromPath(d.netns)
|
||||
} else {
|
||||
targetNs, err = netns.GetFromName(d.netns)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get netns ", d.netns)
|
||||
}
|
||||
defer targetNs.Close()
|
||||
err = netns.Set(targetNs)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "set netns to ", d.netns)
|
||||
}
|
||||
}
|
||||
if destination.IsIPv6() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
|
|
|
@ -3,6 +3,8 @@ package listener
|
|||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
|
@ -13,6 +15,7 @@ import (
|
|||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/metacubex/tfo-go"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
|
@ -37,6 +40,29 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
if l.listenOptions.NetNs != "" {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
currentNs, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get current netns")
|
||||
}
|
||||
defer netns.Set(currentNs)
|
||||
var targetNs netns.NsHandle
|
||||
if strings.HasPrefix(l.listenOptions.NetNs, "/") {
|
||||
targetNs, err = netns.GetFromPath(l.listenOptions.NetNs)
|
||||
} else {
|
||||
targetNs, err = netns.GetFromName(l.listenOptions.NetNs)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get netns ", l.listenOptions.NetNs)
|
||||
}
|
||||
defer targetNs.Close()
|
||||
err = netns.Set(targetNs)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "set netns to ", l.listenOptions.NetNs)
|
||||
}
|
||||
}
|
||||
if l.listenOptions.TCPFastOpen {
|
||||
var tfoConfig tfo.ListenConfig
|
||||
tfoConfig.ListenConfig = listenConfig
|
||||
|
|
|
@ -4,12 +4,16 @@ import (
|
|||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"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/vishvananda/netns"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
||||
|
@ -24,6 +28,29 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
|||
if !udpFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
if l.listenOptions.NetNs != "" {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
currentNs, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get current netns")
|
||||
}
|
||||
defer netns.Set(currentNs)
|
||||
var targetNs netns.NsHandle
|
||||
if strings.HasPrefix(l.listenOptions.NetNs, "/") {
|
||||
targetNs, err = netns.GetFromPath(l.listenOptions.NetNs)
|
||||
} else {
|
||||
targetNs, err = netns.GetFromName(l.listenOptions.NetNs)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get netns ", l.listenOptions.NetNs)
|
||||
}
|
||||
defer targetNs.Close()
|
||||
err = netns.Set(targetNs)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "set netns to ", l.listenOptions.NetNs)
|
||||
}
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -5,7 +5,8 @@ icon: material/new-box
|
|||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [domain_resolver](#domain_resolver)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
|
@ -18,24 +19,25 @@ icon: material/new-box
|
|||
|
||||
```json
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"detour": "",
|
||||
"bind_interface": "",
|
||||
"inet4_bind_address": "",
|
||||
"inet6_bind_address": "",
|
||||
"routing_mark": 0,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"connect_timeout": "",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"netns": "",
|
||||
"domain_resolver": "", // or {}
|
||||
"network_strategy": "default",
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms",
|
||||
"fallback_delay": "",
|
||||
|
||||
// Deprecated
|
||||
"domain_strategy": "prefer_ipv6"
|
||||
"domain_strategy": ""
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -75,6 +77,15 @@ Set netfilter routing mark.
|
|||
|
||||
Reuse listener address.
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
Connect timeout, in golang's Duration format.
|
||||
|
||||
A duration string is a possibly signed sequence of
|
||||
decimal numbers, each with optional fraction and a unit suffix,
|
||||
such as "300ms", "-1.5h" or "2h45m".
|
||||
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
Enable TCP Fast Open.
|
||||
|
@ -91,14 +102,15 @@ Enable TCP Multi Path.
|
|||
|
||||
Enable UDP fragmentation.
|
||||
|
||||
#### connect_timeout
|
||||
#### netns
|
||||
|
||||
Connect timeout, in golang's Duration format.
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
A duration string is a possibly signed sequence of
|
||||
decimal numbers, each with optional fraction and a unit suffix,
|
||||
such as "300ms", "-1.5h" or "2h45m".
|
||||
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux.
|
||||
|
||||
Set network namespace, name or path.
|
||||
|
||||
#### domain_resolver
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ icon: material/new-box
|
|||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [domain_resolver](#domain_resolver)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-delete-clock: [domain_strategy](#domain_strategy)
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
|
@ -18,25 +19,26 @@ icon: material/new-box
|
|||
|
||||
```json
|
||||
{
|
||||
"detour": "upstream-out",
|
||||
"bind_interface": "en0",
|
||||
"inet4_bind_address": "0.0.0.0",
|
||||
"inet6_bind_address": "::",
|
||||
"routing_mark": 1234,
|
||||
"detour": "",
|
||||
"bind_interface": "",
|
||||
"inet4_bind_address": "",
|
||||
"inet6_bind_address": "",
|
||||
"routing_mark": 0,
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"connect_timeout": "",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"netns": "",
|
||||
"domain_resolver": "", // 或 {}
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms",
|
||||
"fallback_delay": "",
|
||||
|
||||
// 废弃的
|
||||
|
||||
"domain_strategy": "prefer_ipv6"
|
||||
"domain_strategy": ""
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -76,6 +78,13 @@ icon: material/new-box
|
|||
|
||||
重用监听地址。
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
连接超时,采用 golang 的 Duration 格式。
|
||||
|
||||
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
|
||||
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
启用 TCP Fast Open。
|
||||
|
@ -92,12 +101,15 @@ icon: material/new-box
|
|||
|
||||
启用 UDP 分段。
|
||||
|
||||
#### connect_timeout
|
||||
#### netns
|
||||
|
||||
连接超时,采用 golang 的 Duration 格式。
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
|
||||
有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux。
|
||||
|
||||
设置网络命名空间,名称或路径。
|
||||
|
||||
#### domain_resolver
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
icon: material/delete-clock
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
|
@ -14,17 +18,18 @@ icon: material/delete-clock
|
|||
|
||||
```json
|
||||
{
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"listen": "",
|
||||
"listen_port": 0,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"detour": "another-in",
|
||||
"udp_timeout": "",
|
||||
"netns": "",
|
||||
"detour": "",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"sniff_timeout": "300ms",
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"sniff_timeout": "",
|
||||
"domain_strategy": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
}
|
||||
```
|
||||
|
@ -72,6 +77,16 @@ 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.
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
icon: material/delete-clock
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [netns](#netns)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-delete-clock: [sniff](#sniff)
|
||||
|
@ -14,17 +18,18 @@ icon: material/delete-clock
|
|||
|
||||
```json
|
||||
{
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"listen": "",
|
||||
"listen_port": 0,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"udp_timeout": "5m",
|
||||
"detour": "another-in",
|
||||
"udp_timeout": "",
|
||||
"netns": "",
|
||||
"detour": "",
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"sniff_timeout": "300ms",
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"sniff_timeout": "",
|
||||
"domain_strategy": "",
|
||||
"udp_disable_domain_unmapping": false
|
||||
}
|
||||
```
|
||||
|
@ -73,6 +78,16 @@ UDP NAT 过期时间。
|
|||
|
||||
默认使用 `5m`。
|
||||
|
||||
#### netns
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux。
|
||||
|
||||
设置网络命名空间,名称或路径。
|
||||
|
||||
#### detour
|
||||
|
||||
如果设置,连接将被转发到指定的入站。
|
||||
|
|
|
@ -68,6 +68,7 @@ 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"`
|
||||
|
|
|
@ -77,6 +77,7 @@ type DialerOptions struct {
|
|||
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"`
|
||||
|
|
Loading…
Add table
Reference in a new issue