client_wireguard.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. for _, u := range used {
  99. if a := wireguardHostAddr(u); a.IsValid() {
  100. taken[a] = struct{}{}
  101. }
  102. }
  103. scopes := []netip.Prefix{prefix}
  104. if allowWidening && prefix.Addr().Is4() && prefix.Bits() > wireguardPoolFloorBits {
  105. if wider, wErr := prefix.Addr().Prefix(wireguardPoolFloorBits); wErr == nil {
  106. scopes = append(scopes, wider)
  107. }
  108. }
  109. for _, scope := range scopes {
  110. addr := scope.Masked().Addr().Next().Next()
  111. for scope.Contains(addr) {
  112. if _, ok := taken[addr]; !ok {
  113. return addr.String() + "/" + hostBits, nil
  114. }
  115. addr = addr.Next()
  116. }
  117. }
  118. return "", common.NewError("wireguard: no free address available in", scopes[len(scopes)-1].String())
  119. }
  120. // normalizeWireguardAllowedIPs validates user-supplied allowedIPs entries and
  121. // canonicalizes them: bare addresses become single-host prefixes, duplicates drop.
  122. func normalizeWireguardAllowedIPs(values []string) ([]string, error) {
  123. out := make([]string, 0, len(values))
  124. seen := make(map[string]struct{}, len(values))
  125. for _, v := range values {
  126. v = strings.TrimSpace(v)
  127. if v == "" {
  128. continue
  129. }
  130. p, err := netip.ParsePrefix(v)
  131. if err != nil {
  132. a, aErr := netip.ParseAddr(v)
  133. if aErr != nil {
  134. return nil, common.NewError("wireguard: invalid allowedIPs entry:", v)
  135. }
  136. p = netip.PrefixFrom(a, a.BitLen())
  137. }
  138. norm := p.String()
  139. if _, dup := seen[norm]; dup {
  140. continue
  141. }
  142. seen[norm] = struct{}{}
  143. out = append(out, norm)
  144. }
  145. return out, nil
  146. }
  147. func wireguardAllowedIPsCollision(entries, used []string) string {
  148. taken := make(map[string]struct{}, len(used))
  149. for _, u := range used {
  150. taken[strings.TrimSpace(u)] = struct{}{}
  151. }
  152. for _, e := range entries {
  153. if _, ok := taken[e]; ok {
  154. return e
  155. }
  156. }
  157. return ""
  158. }
  159. // defaultWireguardClients fills in blank WireGuard credentials for newly added
  160. // clients: a generated keypair when none was provided, a derived public key when
  161. // only a private key was given, and a unique tunnel address allocated from the
  162. // inbound's subnet. It mutates both the typed clients and the parallel raw client
  163. // maps that get persisted into the inbound settings. Existing values are never
  164. // overwritten, so editing a client never rotates its keys.
  165. //
  166. // crossInboundUsed maps AllowedIPs already claimed by clients on every OTHER
  167. // WireGuard/AmneziaWG inbound on this panel to a human-readable description
  168. // of which inbound holds it (see otherTunnelAllowedIPs). It is folded into
  169. // used only AFTER the base subnet is resolved, so an unrelated inbound's
  170. // subnet can never skew this inbound's own base-subnet resolution — it only
  171. // ever narrows which addresses are free to hand out or accept, and lets a
  172. // manual-entry collision name the other inbound instead of just the address.
  173. //
  174. // settingsJSON is checked first for an admin-configured subnetIp/subnetCidr
  175. // (see explicitWireguardSubnetBase) — set explicitly, that always wins.
  176. // Only when it's unset does base fall back to inferring from existing
  177. // clients' own addresses, and finally to defaultWireguardBase, exactly as
  178. // before this field existed.
  179. func defaultWireguardClients(settingsJSON string, existing, clients []model.Client, interfaceClients []any, crossInboundUsed map[string]string) error {
  180. used := make([]string, 0)
  181. for i := range existing {
  182. used = append(used, existing[i].AllowedIPs...)
  183. }
  184. base := explicitWireguardSubnetBase(settingsJSON)
  185. if base == "" {
  186. base = wireguardAllocationBase(used, defaultWireguardBase)
  187. }
  188. for addr := range crossInboundUsed {
  189. used = append(used, addr)
  190. }
  191. for i := range clients {
  192. c := &clients[i]
  193. if c.PrivateKey == "" && c.PublicKey == "" {
  194. priv, pub, err := wgutil.GenerateWireguardKeypair()
  195. if err != nil {
  196. return err
  197. }
  198. c.PrivateKey = priv
  199. c.PublicKey = pub
  200. } else if c.PublicKey == "" && c.PrivateKey != "" {
  201. pub, err := wgutil.PublicKeyFromPrivate(c.PrivateKey)
  202. if err != nil {
  203. return err
  204. }
  205. c.PublicKey = pub
  206. }
  207. if len(c.AllowedIPs) == 0 {
  208. addr, err := allocateWireguardAddress(used, base, true)
  209. if err != nil {
  210. return err
  211. }
  212. c.AllowedIPs = []string{addr}
  213. } else {
  214. normalized, err := normalizeWireguardAllowedIPs(c.AllowedIPs)
  215. if err != nil {
  216. return err
  217. }
  218. if len(normalized) == 0 {
  219. return common.NewError("wireguard: allowedIPs has no usable entry")
  220. }
  221. if hit := wireguardAllowedIPsCollision(normalized, used); hit != "" {
  222. if where := crossInboundUsed[hit]; where != "" {
  223. return common.NewError("wireguard: allowedIPs entry", hit, "is already used by a client on", where)
  224. }
  225. return common.NewError("wireguard: allowedIPs entry already used by another client:", hit)
  226. }
  227. c.AllowedIPs = normalized
  228. }
  229. used = append(used, c.AllowedIPs...)
  230. if i < len(interfaceClients) {
  231. if m, ok := interfaceClients[i].(map[string]any); ok {
  232. m["privateKey"] = c.PrivateKey
  233. m["publicKey"] = c.PublicKey
  234. m["allowedIPs"] = c.AllowedIPs
  235. if c.PreSharedKey != "" {
  236. m["preSharedKey"] = c.PreSharedKey
  237. }
  238. if c.KeepAlive > 0 {
  239. m["keepAlive"] = c.KeepAlive
  240. }
  241. interfaceClients[i] = m
  242. }
  243. }
  244. }
  245. return nil
  246. }