Add bridge outbound
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/bridge"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func NewBridgeService(options *BridgeOptions) (BridgeSession, error) {
|
||||
if options == nil {
|
||||
return nil, E.New("missing bridge options")
|
||||
}
|
||||
serviceOptions := bridge.ServiceOptions{
|
||||
MTU: int(options.MTU),
|
||||
Interface: options.Interface,
|
||||
}
|
||||
if options.Inet4Port != "" {
|
||||
inet4Port, err := netip.ParseAddr(options.Inet4Port)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inet4 port address")
|
||||
}
|
||||
serviceOptions.Inet4Port = inet4Port
|
||||
}
|
||||
if options.Inet6Port != "" {
|
||||
inet6Port, err := netip.ParseAddr(options.Inet6Port)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inet6 port address")
|
||||
}
|
||||
serviceOptions.Inet6Port = inet6Port
|
||||
}
|
||||
logFactory := log.NewDefaultFactory(context.Background(), log.Formatter{BaseTime: time.Now()}, os.Stderr, "", nil, false)
|
||||
serviceOptions.Logger = logFactory.Logger()
|
||||
service, err := bridge.NewService(serviceOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bridgeServiceSession{service}, nil
|
||||
}
|
||||
|
||||
type bridgeServiceSession struct {
|
||||
service *bridge.Service
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) FileDescriptor() int32 {
|
||||
return int32(s.service.FileDescriptor())
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Name() string {
|
||||
return s.service.Name()
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Inet6Active() bool {
|
||||
return s.service.Inet6Active()
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) SetEgress(interfaceName string) error {
|
||||
return s.service.SetEgress(interfaceName)
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Close() error {
|
||||
return s.service.Close()
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//go:build linux
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/protocol/bridge"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func NewBridgeService(options *BridgeOptions) (BridgeSession, error) {
|
||||
if options == nil {
|
||||
return nil, E.New("missing bridge options")
|
||||
}
|
||||
serviceOptions := bridge.ServiceOptions{
|
||||
BridgeName: options.BridgeName,
|
||||
MTU: int(options.MTU),
|
||||
RuleIndex: int(options.RuleIndex),
|
||||
RouteTable: int(options.RouteTable),
|
||||
}
|
||||
if options.Inet4Port != "" {
|
||||
inet4Port, err := netip.ParseAddr(options.Inet4Port)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inet4 port address")
|
||||
}
|
||||
serviceOptions.Inet4Port = inet4Port
|
||||
}
|
||||
if options.Inet6Port != "" {
|
||||
inet6Port, err := netip.ParseAddr(options.Inet6Port)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inet6 port address")
|
||||
}
|
||||
serviceOptions.Inet6Port = inet6Port
|
||||
}
|
||||
logFactory := log.NewDefaultFactory(context.Background(), log.Formatter{BaseTime: time.Now()}, os.Stderr, "", nil, false)
|
||||
serviceOptions.Logger = logFactory.Logger()
|
||||
service, err := bridge.NewService(serviceOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bridgeServiceSession{service}, nil
|
||||
}
|
||||
|
||||
type bridgeServiceSession struct {
|
||||
service *bridge.Service
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) FileDescriptor() int32 {
|
||||
return int32(s.service.FileDescriptor())
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Name() string {
|
||||
return s.service.Name()
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Inet6Active() bool {
|
||||
return s.service.Inet6Active()
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) SetEgress(interfaceName string) error {
|
||||
return s.service.SetEgress(interfaceName)
|
||||
}
|
||||
|
||||
func (s *bridgeServiceSession) Close() error {
|
||||
return s.service.Close()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !linux && !darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
func NewBridgeService(options *BridgeOptions) (BridgeSession, error) {
|
||||
return nil, E.New("bridge service not supported on this platform")
|
||||
}
|
||||
@@ -181,6 +181,14 @@ func (s *platformInterfaceStub) TailscaleHostname() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) UsePlatformBridge() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) CreateBridge(options adapter.BridgeOptions) (adapter.BridgeSession, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) LookupUser(username string) (*adapter.PlatformUser, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -27,6 +27,26 @@ type PlatformInterface interface {
|
||||
LookupSFTPServer() (string, error)
|
||||
ReadSystemSSHHostKey() (string, error)
|
||||
TailscaleHostname() string
|
||||
UsePlatformBridge() bool
|
||||
CreateBridge(options *BridgeOptions) (BridgeSession, error)
|
||||
}
|
||||
|
||||
type BridgeOptions struct {
|
||||
BridgeName string
|
||||
MTU int32
|
||||
Inet4Port string
|
||||
Inet6Port string
|
||||
Interface string
|
||||
RuleIndex int32
|
||||
RouteTable int32
|
||||
}
|
||||
|
||||
type BridgeSession interface {
|
||||
FileDescriptor() int32
|
||||
Name() string
|
||||
Inet6Active() bool
|
||||
SetEgress(interfaceName string) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type PlatformUser struct {
|
||||
|
||||
@@ -285,6 +285,55 @@ func (w *platformInterfaceWrapper) TailscaleHostname() string {
|
||||
return w.iif.TailscaleHostname()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformBridge() bool {
|
||||
return w.iif.UsePlatformBridge()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CreateBridge(options adapter.BridgeOptions) (adapter.BridgeSession, error) {
|
||||
bridgeOptions := &BridgeOptions{
|
||||
BridgeName: options.BridgeName,
|
||||
MTU: int32(options.MTU),
|
||||
Interface: options.Interface,
|
||||
RuleIndex: int32(options.RuleIndex),
|
||||
RouteTable: int32(options.RouteTable),
|
||||
}
|
||||
if options.Inet4Port.IsValid() {
|
||||
bridgeOptions.Inet4Port = options.Inet4Port.String()
|
||||
}
|
||||
if options.Inet6Port.IsValid() {
|
||||
bridgeOptions.Inet6Port = options.Inet6Port.String()
|
||||
}
|
||||
session, err := w.iif.CreateBridge(bridgeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bridgeSessionWrapper{session}, nil
|
||||
}
|
||||
|
||||
type bridgeSessionWrapper struct {
|
||||
session BridgeSession
|
||||
}
|
||||
|
||||
func (w *bridgeSessionWrapper) FileDescriptor() int {
|
||||
return int(w.session.FileDescriptor())
|
||||
}
|
||||
|
||||
func (w *bridgeSessionWrapper) Name() string {
|
||||
return w.session.Name()
|
||||
}
|
||||
|
||||
func (w *bridgeSessionWrapper) Inet6Active() bool {
|
||||
return w.session.Inet6Active()
|
||||
}
|
||||
|
||||
func (w *bridgeSessionWrapper) SetEgress(interfaceName string) error {
|
||||
return w.session.SetEgress(interfaceName)
|
||||
}
|
||||
|
||||
func (w *bridgeSessionWrapper) Close() error {
|
||||
return w.session.Close()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) LookupUser(username string) (*adapter.PlatformUser, error) {
|
||||
platformUser, err := w.iif.LookupUser(username)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user