mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-08-04 06:34:50 +00:00
Fix macOS bridge kernel panic
This commit is contained in:
parent
af0809ed60
commit
a1bc3515db
4 changed files with 26 additions and 22 deletions
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
|
|
@ -23,6 +22,16 @@ var (
|
|||
bridgeInet6LocalBase = netip.MustParseAddr("2001:db8:1::1")
|
||||
)
|
||||
|
||||
// macOS 26.5 (xnu-12377) kernel-panics ("Bounds safety trap") when pf
|
||||
// route-to hands an unfragmented packet larger than one skywalk buflet to a
|
||||
// skywalk-native interface: nx_netif_mbuf_to_kpkt() sizes the allocation
|
||||
// against the TX pool, but nx_netif.c selects the copy routine from the RX
|
||||
// pool's pp_max_frags, so pkt_copy_from_mbuf() writes past the 2048-byte
|
||||
// buflet. utun_ctl_send() accepts writes of any size regardless of the
|
||||
// interface MTU, so the limit must hold before packets are written; utun
|
||||
// reserves UTUN_IF_HEADROOM_SIZE (32) bytes of the buflet, hence 2048-32.
|
||||
const bridgeTunMTUDarwin = 2048 - 32
|
||||
|
||||
type backendDarwin struct {
|
||||
backendBase
|
||||
|
||||
|
|
@ -83,13 +92,12 @@ func (b *backendDarwin) start() error {
|
|||
b.anchorName = "com.apple/sing-box-" + b.tunName
|
||||
tunInterface, err := tun.New(tun.Options{
|
||||
Name: b.tunName,
|
||||
MTU: bridgeTunMTU,
|
||||
MTU: bridgeTunMTUDarwin,
|
||||
AutoRoute: false,
|
||||
InterfaceMonitor: b.networkManager.InterfaceMonitor(),
|
||||
Logger: b.logger,
|
||||
EXP_ExternalConfiguration: true,
|
||||
EXP_MultiPendingPackets: true,
|
||||
EXP_SendMsgX: true,
|
||||
})
|
||||
if err != nil {
|
||||
return E.Cause(err, "create bridge tun")
|
||||
|
|
@ -126,7 +134,7 @@ func (b *backendDarwin) start() error {
|
|||
func (b *backendDarwin) startPlatform() error {
|
||||
session, err := b.platform.CreateBridge(adapter.BridgeOptions{
|
||||
BridgeName: b.bridgeName,
|
||||
MTU: bridgeTunMTU,
|
||||
MTU: bridgeTunMTUDarwin,
|
||||
Inet4Port: b.inet4Port,
|
||||
Inet6Port: b.inet6Port,
|
||||
Interface: b.boundInterface,
|
||||
|
|
@ -141,7 +149,7 @@ func (b *backendDarwin) startPlatform() error {
|
|||
}
|
||||
tunInterface, err := tun.New(tun.Options{
|
||||
Name: b.tunName,
|
||||
MTU: bridgeTunMTU,
|
||||
MTU: bridgeTunMTUDarwin,
|
||||
FileDescriptor: session.FileDescriptor(),
|
||||
Logger: b.logger,
|
||||
EXP_ExternalConfiguration: true,
|
||||
|
|
@ -207,10 +215,8 @@ func (b *backendDarwin) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Zero tells the dispatcher not to clamp the TCP MSS or fragment; pf and the
|
||||
// host kernel do both on the forwarding path instead (see buildBridgeAnchorRules).
|
||||
func (b *backendDarwin) PortMTU() uint32 {
|
||||
return 0
|
||||
return bridgeTunMTUDarwin
|
||||
}
|
||||
|
||||
func (b *backendDarwin) WritePackets(packets [][]byte) error {
|
||||
|
|
@ -224,18 +230,8 @@ func (b *backendDarwin) WritePackets(packets [][]byte) error {
|
|||
packets = packets[len(chunk):]
|
||||
batch := b.writeBatch[:0]
|
||||
for _, packet := range chunk {
|
||||
if len(packet) == 0 || len(packet) > maxPacketLength {
|
||||
continue
|
||||
}
|
||||
ipVersion := header.IPVersion(packet)
|
||||
if ipVersion != header.IPv4Version && ipVersion != header.IPv6Version {
|
||||
continue
|
||||
}
|
||||
batch = append(batch, buf.As(packet))
|
||||
}
|
||||
if len(batch) == 0 {
|
||||
continue
|
||||
}
|
||||
err := b.batchTUN.BatchWrite(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -260,8 +260,6 @@ func (b *backendLinux) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Zero tells the dispatcher not to clamp the TCP MSS or fragment; the host kernel
|
||||
// does both on the forwarding path instead (see setupBridgeClampRules).
|
||||
func (b *backendLinux) PortMTU() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//go:build linux || darwin || (windows && (amd64 || 386))
|
||||
|
||||
//nolint:unused
|
||||
package bridge
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -17,9 +17,18 @@ func buildBridgeAnchorRules(ruleLogger logger.ContextLogger, tunName string, egr
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// The flowswitch aggregates forwarded TCP into packets larger than the tun
|
||||
// MTU, and pf_route() only fragments when they exceed the egress MTU: a
|
||||
// large-MTU utun target feeds them whole into the overflow described at
|
||||
// bridgeTunMTUDarwin.
|
||||
if egressInterface.Flags&net.FlagBroadcast == 0 || egressInterface.Flags&net.FlagLoopback != 0 ||
|
||||
egressInterface.Flags&net.FlagPointToPoint != 0 {
|
||||
ruleLogger.Error("bridge egress ", egress, " is not a physical interface, dropping forwarded traffic")
|
||||
return nil
|
||||
}
|
||||
mtu := egressInterface.MTU
|
||||
if mtu < 576 || mtu > bridgeTunMTU {
|
||||
mtu = bridgeTunMTU
|
||||
if mtu < 576 || mtu > bridgeTunMTUDarwin {
|
||||
mtu = bridgeTunMTUDarwin
|
||||
}
|
||||
localPrefixes, inet4Interfaces, inet6Interfaces := collectLocalSegments(egress, boundInterface, inet4Port.IsValid(), inet6Port.IsValid())
|
||||
var rules []pfAnchorRule
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue