feat: add ip-stack option for openvpn outbound

This commit is contained in:
wwqgtxx committed 2026-08-04 23:09:11 +08:00
1 parent 1265cb9a99
commit d46944f32b
2 files changed
+18 -10

No files matched your search

+15 -10
View File
@@ -18,8 +18,6 @@ import (
"github.com/metacubex/mihomo/log"
ovpn "github.com/metacubex/mihomo/transport/openvpn"
wireguard "github.com/metacubex/sing-wireguard"
M "github.com/metacubex/sing/common/metadata"
"golang.org/x/sync/semaphore"
)
@@ -28,7 +26,7 @@ type OpenVPN struct {
option *OpenVPNOption
config *ovpn.ClientConfig
tunDevice wireguard.Device
tunDevice ipStack
client *ovpn.Client
resolver resolver.Resolver
dns []dns.NameServer
@@ -67,6 +65,8 @@ type OpenVPNOption struct {
MTU int `proxy:"mtu,omitempty"`
UDP bool `proxy:"udp,omitempty"`
IPStack IPStackOption `proxy:"ip-stack,omitempty"`
RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"`
Dns []string `proxy:"dns,omitempty"`
}
@@ -75,6 +75,10 @@ func NewOpenVPN(option OpenVPNOption) (*OpenVPN, error) {
if option.HandshakeTimeout < 0 {
return nil, errors.New("openvpn handshake timeout must be non-negative")
}
option.IPStack.normalize()
if err := option.IPStack.validate(); err != nil {
return nil, err
}
cfg := &ovpn.ClientConfig{
RemoteHost: option.Server,
RemotePort: uint16(option.Port),
@@ -143,10 +147,10 @@ func (o *OpenVPN) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Co
}
options := o.DialOptions()
options = append(options, dialer.WithResolver(r))
options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: tunDevice}))
options = append(options, dialer.WithNetDialer(ipStackNetDialer{stack: tunDevice}))
conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress())
} else {
conn, err = tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
conn, err = tunDevice.DialTCP(ctx, "tcp", netip.AddrPort{}, metadata.AddrPort())
}
if err != nil {
return nil, err
@@ -166,7 +170,8 @@ func (o *OpenVPN) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
if err = o.resolveUDP(ctx, metadata, r); err != nil {
return nil, err
}
pc, err = tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
// The ipStack contract guarantees that a generic UDP wildcard supports both address families.
pc, err = tunDevice.ListenUDP(ctx, "udp", netip.AddrPort{})
if err != nil {
return nil, err
}
@@ -229,7 +234,7 @@ func (o *OpenVPN) Close() error {
return nil
}
func (o *OpenVPN) run(ctx context.Context) (wireguard.Device, resolver.Resolver, error) {
func (o *OpenVPN) run(ctx context.Context) (ipStack, resolver.Resolver, error) {
runCtx, cancel := context.WithCancel(ctx)
stop := contextutils.AfterFunc(o.runCtx, cancel)
defer func() {
@@ -259,7 +264,7 @@ func (o *OpenVPN) run(ctx context.Context) (wireguard.Device, resolver.Resolver,
if o.option.HandshakeTimeout > 0 {
type runResult struct {
tunDevice wireguard.Device
tunDevice ipStack
resolver resolver.Resolver
err error
}
@@ -288,7 +293,7 @@ func (o *OpenVPN) run(ctx context.Context) (wireguard.Device, resolver.Resolver,
return o.startLocked(runCtx)
}
func (o *OpenVPN) startLocked(handshakeCtx context.Context) (wireguard.Device, resolver.Resolver, error) {
func (o *OpenVPN) startLocked(handshakeCtx context.Context) (ipStack, resolver.Resolver, error) {
packetIO, err := o.openPacketIO(handshakeCtx)
if err != nil {
return nil, nil, fmt.Errorf("connect OpenVPN server: %w", err)
@@ -309,7 +314,7 @@ func (o *OpenVPN) startLocked(handshakeCtx context.Context) (wireguard.Device, r
if mtu == 0 {
mtu = 1500
}
tunDevice, err := wireguard.NewStackDevice(push.Prefixes, uint32(mtu))
tunDevice, err := newIPStack(o.option.IPStack, push.Prefixes, uint32(mtu))
if err != nil {
_ = client.Close()
return nil, nil, fmt.Errorf("create OpenVPN stack device: %w", err)
+3
View File
@@ -1446,6 +1446,9 @@ proxies: # socks5
# handshake-timeout: 30 # 单位为秒;配置后握手时不受外层连接超时影响;默认值为 0,表示仅使用外层连接超时
# mtu: 1500
udp: true
# ip-stack:
# mode: auto # options: auto, gvisor, mips; auto uses gVisor when compiled in and mihomo IP stack (MIPS) otherwise
# congestion-controller: cubic # TCP congestion controller: cubic, reno, or bbr; ignored by gVisor
# 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接
# dialer-proxy: "ss1"
# remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false