github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/wireguard.go (about) 1 //go:build with_wireguard 2 3 package outbound 4 5 import ( 6 "context" 7 "encoding/base64" 8 "encoding/hex" 9 "fmt" 10 "net" 11 "net/netip" 12 "strings" 13 14 "github.com/sagernet/sing-box/adapter" 15 "github.com/sagernet/sing-box/common/dialer" 16 C "github.com/sagernet/sing-box/constant" 17 "github.com/sagernet/sing-box/log" 18 "github.com/sagernet/sing-box/option" 19 "github.com/sagernet/sing-box/transport/wireguard" 20 "github.com/sagernet/sing-dns" 21 "github.com/sagernet/sing-tun" 22 E "github.com/sagernet/sing/common/exceptions" 23 M "github.com/sagernet/sing/common/metadata" 24 N "github.com/sagernet/sing/common/network" 25 "github.com/sagernet/sing/common/x/list" 26 "github.com/sagernet/sing/service" 27 "github.com/sagernet/sing/service/pause" 28 "github.com/sagernet/wireguard-go/conn" 29 "github.com/sagernet/wireguard-go/device" 30 ) 31 32 var ( 33 _ adapter.Outbound = (*WireGuard)(nil) 34 _ adapter.InterfaceUpdateListener = (*WireGuard)(nil) 35 ) 36 37 type WireGuard struct { 38 myOutboundAdapter 39 ctx context.Context 40 workers int 41 peers []wireguard.PeerConfig 42 useStdNetBind bool 43 listener N.Dialer 44 ipcConf string 45 46 pauseManager pause.Manager 47 pauseCallback *list.Element[pause.Callback] 48 bind conn.Bind 49 device *device.Device 50 tunDevice wireguard.Device 51 } 52 53 func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) { 54 outbound := &WireGuard{ 55 myOutboundAdapter: myOutboundAdapter{ 56 protocol: C.TypeWireGuard, 57 network: options.Network.Build(), 58 router: router, 59 logger: logger, 60 tag: tag, 61 dependencies: withDialerDependency(options.DialerOptions), 62 }, 63 ctx: ctx, 64 workers: options.Workers, 65 pauseManager: service.FromContext[pause.Manager](ctx), 66 } 67 peers, err := wireguard.ParsePeers(options) 68 if err != nil { 69 return nil, err 70 } 71 outbound.peers = peers 72 if len(options.LocalAddress) == 0 { 73 return nil, E.New("missing local address") 74 } 75 if options.GSO { 76 if options.GSO && options.Detour != "" { 77 return nil, E.New("gso is conflict with detour") 78 } 79 options.IsWireGuardListener = true 80 outbound.useStdNetBind = true 81 } 82 listener, err := dialer.New(router, options.DialerOptions) 83 if err != nil { 84 return nil, err 85 } 86 outbound.listener = listener 87 var privateKey string 88 { 89 bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey) 90 if err != nil { 91 return nil, E.Cause(err, "decode private key") 92 } 93 privateKey = hex.EncodeToString(bytes) 94 } 95 outbound.ipcConf = "private_key=" + privateKey 96 mtu := options.MTU 97 if mtu == 0 { 98 mtu = 1408 99 } 100 var wireTunDevice wireguard.Device 101 if !options.SystemInterface && tun.WithGVisor { 102 wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu) 103 } else { 104 wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu, options.GSO) 105 } 106 if err != nil { 107 return nil, E.Cause(err, "create WireGuard device") 108 } 109 outbound.tunDevice = wireTunDevice 110 return outbound, nil 111 } 112 113 func (w *WireGuard) Start() error { 114 err := wireguard.ResolvePeers(w.ctx, w.router, w.peers) 115 if err != nil { 116 return err 117 } 118 var bind conn.Bind 119 if w.useStdNetBind { 120 bind = conn.NewStdNetBind(w.listener.(dialer.WireGuardListener)) 121 } else { 122 var ( 123 isConnect bool 124 connectAddr netip.AddrPort 125 reserved [3]uint8 126 ) 127 peerLen := len(w.peers) 128 if peerLen == 1 { 129 isConnect = true 130 connectAddr = w.peers[0].Endpoint 131 reserved = w.peers[0].Reserved 132 } 133 bind = wireguard.NewClientBind(w.ctx, w, w.listener, isConnect, connectAddr, reserved) 134 } 135 wgDevice := device.NewDevice(w.tunDevice, bind, &device.Logger{ 136 Verbosef: func(format string, args ...interface{}) { 137 w.logger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) 138 }, 139 Errorf: func(format string, args ...interface{}) { 140 w.logger.Error(fmt.Sprintf(strings.ToLower(format), args...)) 141 }, 142 }, w.workers) 143 ipcConf := w.ipcConf 144 for _, peer := range w.peers { 145 ipcConf += peer.GenerateIpcLines() 146 } 147 err = wgDevice.IpcSet(ipcConf) 148 if err != nil { 149 return E.Cause(err, "setup wireguard: \n", ipcConf) 150 } 151 w.device = wgDevice 152 w.pauseCallback = w.pauseManager.RegisterCallback(w.onPauseUpdated) 153 return w.tunDevice.Start() 154 } 155 156 func (w *WireGuard) Close() error { 157 if w.device != nil { 158 w.device.Close() 159 } 160 if w.pauseCallback != nil { 161 w.pauseManager.UnregisterCallback(w.pauseCallback) 162 } 163 w.tunDevice.Close() 164 return nil 165 } 166 167 func (w *WireGuard) InterfaceUpdated() { 168 w.device.BindUpdate() 169 return 170 } 171 172 func (w *WireGuard) onPauseUpdated(event int) { 173 switch event { 174 case pause.EventDevicePaused: 175 w.device.Down() 176 case pause.EventDeviceWake: 177 w.device.Up() 178 } 179 } 180 181 func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 182 switch network { 183 case N.NetworkTCP: 184 w.logger.InfoContext(ctx, "outbound connection to ", destination) 185 case N.NetworkUDP: 186 w.logger.InfoContext(ctx, "outbound packet connection to ", destination) 187 } 188 if destination.IsFqdn() { 189 destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn) 190 if err != nil { 191 return nil, err 192 } 193 return N.DialSerial(ctx, w.tunDevice, network, destination, destinationAddresses) 194 } 195 return w.tunDevice.DialContext(ctx, network, destination) 196 } 197 198 func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 199 w.logger.InfoContext(ctx, "outbound packet connection to ", destination) 200 if destination.IsFqdn() { 201 destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn) 202 if err != nil { 203 return nil, err 204 } 205 packetConn, _, err := N.ListenSerial(ctx, w.tunDevice, destination, destinationAddresses) 206 if err != nil { 207 return nil, err 208 } 209 return packetConn, err 210 } 211 return w.tunDevice.ListenPacket(ctx, destination) 212 } 213 214 func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 215 return NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) 216 } 217 218 func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 219 return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) 220 }