1
0

client_wireguard.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package service
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/netip"
  6. "strconv"
  7. "strings"
  8. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  9. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  10. wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
  11. )
  12. const defaultWireguardBase = "10.0.0.0/24"
  13. // wireguardSubnetSettings is the subset of a WireGuard inbound's top-level
  14. // settings JSON this package cares about for subnet resolution. Unlike
  15. // AmneziaWG (whose whole settings shape is a typed struct in
  16. // internal/amneziawg), plain WireGuard has no dedicated Go struct on this
  17. // fork's side at all -- everything else is handled as untyped
  18. // map[string]any -- so this stays a narrow, local decode rather than
  19. // introducing a full struct just for two fields.
  20. type wireguardSubnetSettings struct {
  21. SubnetIP string `json:"subnetIp"`
  22. SubnetCIDR int `json:"subnetCidr"`
  23. }
  24. // explicitWireguardSubnetBase resolves an admin-configured subnet base out
  25. // of settingsJSON's own subnetIp/subnetCidr fields, mirroring AmneziaWG's
  26. // defaultAmneziaWGSubnetBases. Returns "" when either field is unset/empty
  27. // or doesn't parse as a valid prefix -- callers fall back to
  28. // wireguardAllocationBase's existing infer-from-clients behavior in that
  29. // case, so an inbound saved before this field existed (or one that simply
  30. // never set it) keeps behaving exactly as it always has.
  31. func explicitWireguardSubnetBase(settingsJSON string) string {
  32. var parsed wireguardSubnetSettings
  33. if err := json.Unmarshal([]byte(settingsJSON), &parsed); err != nil {
  34. return ""
  35. }
  36. ip := strings.TrimSpace(parsed.SubnetIP)
  37. if ip == "" || parsed.SubnetCIDR <= 0 {
  38. return ""
  39. }
  40. base := fmt.Sprintf("%s/%d", ip, parsed.SubnetCIDR)
  41. if _, err := netip.ParsePrefix(base); err != nil {
  42. return ""
  43. }
  44. return base
  45. }
  46. func keepAliveStr(seconds int) string {
  47. if seconds <= 0 {
  48. return ""
  49. }
  50. return strconv.Itoa(seconds)
  51. }
  52. func wireguardHostAddr(s string) netip.Addr {
  53. s = strings.TrimSpace(s)
  54. if s == "" {
  55. return netip.Addr{}
  56. }
  57. if p, err := netip.ParsePrefix(s); err == nil {
  58. return p.Addr()
  59. }
  60. if a, err := netip.ParseAddr(s); err == nil {
  61. return a
  62. }
  63. return netip.Addr{}
  64. }
  65. func wireguardAllocationBase(used []string, fallback string) string {
  66. for _, u := range used {
  67. a := wireguardHostAddr(u)
  68. if !a.IsValid() || !a.Is4() || a.IsUnspecified() {
  69. continue
  70. }
  71. if p, err := a.Prefix(24); err == nil {
  72. return p.String()
  73. }
  74. }
  75. return fallback
  76. }
  77. const wireguardPoolFloorBits = 16
  78. // allocateWireguardAddress returns the first free single-host address in base
  79. // not already in used, starting at the second host (the server holds the first).
  80. //
  81. // allowWidening retries in the containing /16 once base's pool is exhausted.
  82. // True for Xray-native WireGuard, whose AllowedIPs aren't tied to a kernel
  83. // interface subnet; AmneziaWG must pass false and fail loudly instead, since an
  84. // address outside its interface's own Address would be silently unroutable.
  85. func allocateWireguardAddress(used []string, base string, allowWidening bool) (string, error) {
  86. if base == "" {
  87. base = defaultWireguardBase
  88. }
  89. prefix, err := netip.ParsePrefix(base)
  90. if err != nil {
  91. return "", err
  92. }
  93. hostBits := "32"
  94. if prefix.Addr().Is6() {
  95. hostBits = "128"
  96. }
  97. taken := make(map[netip.Addr]struct{}, len(used))
  98. var wide []netip.Prefix
  99. for _, u := range used {
  100. p, ok := wireguardClaimedPrefix(u)
  101. if !ok {
  102. continue
  103. }
  104. if p.IsSingleIP() {
  105. taken[p.Addr()] = struct{}{}
  106. } else {
  107. wide = append(wide, p)
  108. }
  109. }
  110. isTaken := func(a netip.Addr) bool {
  111. if _, ok := taken[a]; ok {
  112. return true
  113. }
  114. for _, p := range wide {
  115. if p.Contains(a) {
  116. return true
  117. }
  118. }
  119. return false
  120. }
  121. scopes := []netip.Prefix{prefix}
  122. if allowWidening && prefix.Addr().Is4() && prefix.Bits() > wireguardPoolFloorBits {
  123. if wider, wErr := prefix.Addr().Prefix(wireguardPoolFloorBits); wErr == nil {
  124. scopes = append(scopes, wider)
  125. }
  126. }
  127. for _, scope := range scopes {
  128. addr := scope.Masked().Addr().Next().Next()
  129. for scope.Contains(addr) {
  130. if !isTaken(addr) {
  131. return addr.String() + "/" + hostBits, nil
  132. }
  133. addr = addr.Next()
  134. }
  135. }
  136. return "", common.NewError("wireguard: no free address available in", scopes[len(scopes)-1].String())
  137. }
  138. // normalizeWireguardAllowedIPs validates user-supplied allowedIPs entries and
  139. // canonicalizes them: bare addresses become single-host prefixes, duplicates drop.
  140. func normalizeWireguardAllowedIPs(values []string) ([]string, error) {
  141. out := make([]string, 0, len(values))
  142. seen := make(map[string]struct{}, len(values))
  143. for _, v := range values {
  144. v = strings.TrimSpace(v)
  145. if v == "" {
  146. continue
  147. }
  148. p, err := netip.ParsePrefix(v)
  149. if err != nil {
  150. a, aErr := netip.ParseAddr(v)
  151. if aErr != nil {
  152. return nil, common.NewError("wireguard: invalid allowedIPs entry:", v)
  153. }
  154. p = netip.PrefixFrom(a, a.BitLen())
  155. }
  156. norm := p.String()
  157. if _, dup := seen[norm]; dup {
  158. continue
  159. }
  160. seen[norm] = struct{}{}
  161. out = append(out, norm)
  162. }
  163. return out, nil
  164. }
  165. // wireguardClaimedPrefix is the masked range an allowedIPs entry claims, as xray
  166. // reads it. A /0 default route claims no tunnel address, as legacy peers carry it.
  167. func wireguardClaimedPrefix(s string) (netip.Prefix, bool) {
  168. s = strings.TrimSpace(s)
  169. p, err := netip.ParsePrefix(s)
  170. if err != nil {
  171. a, aErr := netip.ParseAddr(s)
  172. if aErr != nil {
  173. return netip.Prefix{}, false
  174. }
  175. p = netip.PrefixFrom(a, a.BitLen())
  176. }
  177. if p.Bits() == 0 {
  178. return netip.Prefix{}, false
  179. }
  180. return p.Masked(), true
  181. }
  182. // wireguardAllowedIPsOverlap returns the first entry whose range overlaps a used
  183. // one, and that used entry; xray routes and attributes by containment, not equality.
  184. func wireguardAllowedIPsOverlap(entries, used []string) (entry, taken string) {
  185. usedPrefixes := make([]netip.Prefix, len(used))
  186. usedOK := make([]bool, len(used))
  187. for i, u := range used {
  188. usedPrefixes[i], usedOK[i] = wireguardClaimedPrefix(u)
  189. }
  190. for _, e := range entries {
  191. ep, ok := wireguardClaimedPrefix(e)
  192. if !ok {
  193. continue
  194. }
  195. for i, up := range usedPrefixes {
  196. if usedOK[i] && ep.Overlaps(up) {
  197. return e, used[i]
  198. }
  199. }
  200. }
  201. return "", ""
  202. }
  203. // defaultWireguardClients fills in blank WireGuard credentials for newly added
  204. // clients: a generated keypair when none was provided, a derived public key when
  205. // only a private key was given, and a unique tunnel address allocated from the
  206. // inbound's subnet. It mutates both the typed clients and the parallel raw client
  207. // maps that get persisted into the inbound settings. Existing values are never
  208. // overwritten, so editing a client never rotates its keys.
  209. //
  210. // crossInboundUsed maps AllowedIPs already claimed by clients on every OTHER
  211. // WireGuard/AmneziaWG inbound on this panel to a human-readable description
  212. // of which inbound holds it (see otherTunnelAllowedIPs). It is folded into
  213. // used only AFTER the base subnet is resolved, so an unrelated inbound's
  214. // subnet can never skew this inbound's own base-subnet resolution — it only
  215. // ever narrows which addresses are free to hand out or accept, and lets a
  216. // manual-entry collision name the other inbound instead of just the address.
  217. //
  218. // settingsJSON is checked first for an admin-configured subnetIp/subnetCidr
  219. // (see explicitWireguardSubnetBase) — set explicitly, that always wins.
  220. // Only when it's unset does base fall back to inferring from existing
  221. // clients' own addresses, and finally to defaultWireguardBase, exactly as
  222. // before this field existed.
  223. func defaultWireguardClients(settingsJSON string, existing, clients []model.Client, interfaceClients []any, crossInboundUsed map[string]string) error {
  224. used := make([]string, 0)
  225. for i := range existing {
  226. used = append(used, existing[i].AllowedIPs...)
  227. }
  228. base := explicitWireguardSubnetBase(settingsJSON)
  229. if base == "" {
  230. base = wireguardAllocationBase(used, defaultWireguardBase)
  231. }
  232. for addr := range crossInboundUsed {
  233. used = append(used, addr)
  234. }
  235. for i := range clients {
  236. c := &clients[i]
  237. if c.PrivateKey == "" && c.PublicKey == "" {
  238. priv, pub, err := wgutil.GenerateWireguardKeypair()
  239. if err != nil {
  240. return err
  241. }
  242. c.PrivateKey = priv
  243. c.PublicKey = pub
  244. } else if c.PublicKey == "" && c.PrivateKey != "" {
  245. pub, err := wgutil.PublicKeyFromPrivate(c.PrivateKey)
  246. if err != nil {
  247. return err
  248. }
  249. c.PublicKey = pub
  250. }
  251. if len(c.AllowedIPs) == 0 {
  252. addr, err := allocateWireguardAddress(used, base, true)
  253. if err != nil {
  254. return err
  255. }
  256. c.AllowedIPs = []string{addr}
  257. } else {
  258. normalized, err := normalizeWireguardAllowedIPs(c.AllowedIPs)
  259. if err != nil {
  260. return err
  261. }
  262. if len(normalized) == 0 {
  263. return common.NewError("wireguard: allowedIPs has no usable entry")
  264. }
  265. if entry, taken := wireguardAllowedIPsOverlap(normalized, used); taken != "" {
  266. if where := crossInboundUsed[taken]; where != "" {
  267. return common.NewError("wireguard: allowedIPs entry", entry, "overlaps", taken, "used by a client on", where)
  268. }
  269. return common.NewError("wireguard: allowedIPs entry", entry, "overlaps", taken, "used by another client")
  270. }
  271. c.AllowedIPs = normalized
  272. }
  273. used = append(used, c.AllowedIPs...)
  274. if i < len(interfaceClients) {
  275. if m, ok := interfaceClients[i].(map[string]any); ok {
  276. m["privateKey"] = c.PrivateKey
  277. m["publicKey"] = c.PublicKey
  278. m["allowedIPs"] = c.AllowedIPs
  279. if c.PreSharedKey != "" {
  280. m["preSharedKey"] = c.PreSharedKey
  281. }
  282. if ka := c.KeepAliveSeconds(); ka > 0 {
  283. m["keepAlive"] = ka
  284. }
  285. interfaceClients[i] = m
  286. }
  287. }
  288. }
  289. return nil
  290. }