2026-07-08 00:10:00 +08:00
package bridge
import (
"context"
"net"
"net/netip"
2026-07-08 11:14:42 +08:00
"slices"
2026-07-08 00:10:00 +08:00
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
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 RegisterOutbound ( registry * outbound . Registry ) {
outbound . Register [ option . BridgeOutboundOptions ]( registry , C . TypeBridge , NewOutbound )
}
var (
2026-07-08 11:14:42 +08:00
_ adapter . Outbound = ( * Outbound )( nil )
_ adapter . FlowOutbound = ( * Outbound )( nil )
_ adapter . OutboundWithPreferredRoutes = ( * Outbound )( nil )
_ adapter . Lifecycle = ( * Outbound )( nil )
2026-07-08 00:10:00 +08:00
)
type Backend interface {
adapter . Lifecycle
tun . Port
}
type Outbound struct {
outbound . Adapter
2026-07-08 11:14:42 +08:00
logger log . ContextLogger
networkManager adapter . NetworkManager
platformInterface adapter . PlatformInterface
backend Backend
2026-07-08 00:10:00 +08:00
}
func NewOutbound ( ctx context . Context , router adapter . Router , logger log . ContextLogger , tag string , options option . BridgeOutboundOptions ) ( adapter . Outbound , error ) {
networkManager := service . FromContext [ adapter . NetworkManager ]( ctx )
outboundBackend , err := newBackend ( ctx , logger , networkManager , tag , options )
if err != nil {
return nil , err
}
return & Outbound {
2026-07-08 11:14:42 +08:00
Adapter : outbound . NewAdapter ( C . TypeBridge , tag , [] string { N . NetworkTCP , N . NetworkUDP , N . NetworkICMP }, nil ),
logger : logger ,
networkManager : networkManager ,
platformInterface : service . FromContext [ adapter . PlatformInterface ]( ctx ),
backend : outboundBackend ,
2026-07-08 00:10:00 +08:00
}, nil
}
func ( o * Outbound ) Start ( stage adapter . StartStage ) error {
return o . backend . Start ( stage )
}
func ( o * Outbound ) Close () error {
return o . backend . Close ()
}
2026-07-08 11:14:42 +08:00
func ( o * Outbound ) PreferredDomain ( metadata * adapter . InboundContext , domain string ) bool {
return false
}
func ( o * Outbound ) PreferredAddress ( metadata * adapter . InboundContext , address netip . Addr ) bool {
return metadata . PreMatch && ! o . isLocalDestination ( address )
}
func ( o * Outbound ) PreMatchFlow ( network string , destination netip . Addr ) adapter . PreMatchAction {
if o . isLocalDestination ( destination ) {
o . logger . Warn ( "rejected connection to local destination " , destination , ": traffic to local addresses is not supported by bridge, exclude them in route rules" )
return adapter . PreMatchReject
}
return adapter . PreMatchFlow
}
func ( o * Outbound ) isLocalDestination ( destination netip . Addr ) bool {
if ! destination . IsValid () {
return false
}
destination = destination . Unmap ()
if destination . IsLoopback () || destination . IsUnspecified () {
return true
}
if o . platformInterface != nil && slices . Contains ( o . platformInterface . MyInterfaceAddress (), destination ) {
return true
}
for _ , netInterface := range o . networkManager . InterfaceFinder (). Interfaces () {
for _ , prefix := range netInterface . Addresses {
if prefix . Addr () == destination {
return true
}
}
}
return false
2026-07-08 00:10:00 +08:00
}
func ( o * Outbound ) PortAddresses () ( netip . Addr , netip . Addr ) {
return o . backend . PortAddresses ()
}
func ( o * Outbound ) PortMTU () uint32 {
return o . backend . PortMTU ()
}
2026-07-08 17:38:33 +08:00
func ( o * Outbound ) PortSelectorRange () ( uint16 , uint16 ) {
if rangedBackend , isRanged := o . backend .( tun . PortWithSelectorRange ); isRanged {
return rangedBackend . PortSelectorRange ()
}
return 0 , 0
}
2026-07-08 00:10:00 +08:00
func ( o * Outbound ) AttachReturn ( returnPath tun . Return ) error {
return o . backend . AttachReturn ( returnPath )
}
func ( o * Outbound ) DetachReturn ( returnPath tun . Return ) error {
return o . backend . DetachReturn ( returnPath )
}
func ( o * Outbound ) WritePackets ( packets [][] byte ) error {
return o . backend . WritePackets ( packets )
}
func ( o * Outbound ) DialContext ( ctx context . Context , network string , destination M . Socksaddr ) ( net . Conn , error ) {
2026-07-08 11:14:42 +08:00
return nil , E . New ( "only L3 traffic is supported by bridge" )
2026-07-08 00:10:00 +08:00
}
func ( o * Outbound ) ListenPacket ( ctx context . Context , destination M . Socksaddr ) ( net . PacketConn , error ) {
2026-07-08 11:14:42 +08:00
return nil , E . New ( "only L3 traffic is supported by bridge" )
2026-07-08 00:10:00 +08:00
}