client_wireguard.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package service
  2. import (
  3. "net/netip"
  4. "strconv"
  5. "strings"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  8. wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
  9. )
  10. const defaultWireguardBase = "10.0.0.0/24"
  11. func keepAliveStr(seconds int) string {
  12. if seconds <= 0 {
  13. return ""
  14. }
  15. return strconv.Itoa(seconds)
  16. }
  17. func wireguardHostAddr(s string) netip.Addr {
  18. s = strings.TrimSpace(s)
  19. if s == "" {
  20. return netip.Addr{}
  21. }
  22. if p, err := netip.ParsePrefix(s); err == nil {
  23. return p.Addr()
  24. }
  25. if a, err := netip.ParseAddr(s); err == nil {
  26. return a
  27. }
  28. return netip.Addr{}
  29. }
  30. func wireguardAllocationBase(used []string, fallback string) string {
  31. for _, u := range used {
  32. a := wireguardHostAddr(u)
  33. if !a.IsValid() || !a.Is4() || a.IsUnspecified() {
  34. continue
  35. }
  36. if p, err := a.Prefix(24); err == nil {
  37. return p.String()
  38. }
  39. }
  40. return fallback
  41. }
  42. const wireguardPoolFloorBits = 16
  43. func allocateWireguardAddress(used []string, base string) (string, error) {
  44. if base == "" {
  45. base = defaultWireguardBase
  46. }
  47. prefix, err := netip.ParsePrefix(base)
  48. if err != nil {
  49. return "", err
  50. }
  51. taken := make(map[netip.Addr]struct{}, len(used))
  52. for _, u := range used {
  53. if a := wireguardHostAddr(u); a.IsValid() {
  54. taken[a] = struct{}{}
  55. }
  56. }
  57. scopes := []netip.Prefix{prefix}
  58. if prefix.Addr().Is4() && prefix.Bits() > wireguardPoolFloorBits {
  59. if wider, wErr := prefix.Addr().Prefix(wireguardPoolFloorBits); wErr == nil {
  60. scopes = append(scopes, wider)
  61. }
  62. }
  63. for _, scope := range scopes {
  64. addr := scope.Masked().Addr().Next().Next()
  65. for scope.Contains(addr) {
  66. if _, ok := taken[addr]; !ok {
  67. return addr.String() + "/32", nil
  68. }
  69. addr = addr.Next()
  70. }
  71. }
  72. return "", common.NewError("wireguard: no free address available in", scopes[len(scopes)-1].String())
  73. }
  74. // normalizeWireguardAllowedIPs validates user-supplied allowedIPs entries and
  75. // canonicalizes them: bare addresses become single-host prefixes, duplicates drop.
  76. func normalizeWireguardAllowedIPs(values []string) ([]string, error) {
  77. out := make([]string, 0, len(values))
  78. seen := make(map[string]struct{}, len(values))
  79. for _, v := range values {
  80. v = strings.TrimSpace(v)
  81. if v == "" {
  82. continue
  83. }
  84. p, err := netip.ParsePrefix(v)
  85. if err != nil {
  86. a, aErr := netip.ParseAddr(v)
  87. if aErr != nil {
  88. return nil, common.NewError("wireguard: invalid allowedIPs entry:", v)
  89. }
  90. p = netip.PrefixFrom(a, a.BitLen())
  91. }
  92. norm := p.String()
  93. if _, dup := seen[norm]; dup {
  94. continue
  95. }
  96. seen[norm] = struct{}{}
  97. out = append(out, norm)
  98. }
  99. return out, nil
  100. }
  101. func wireguardAllowedIPsCollision(entries, used []string) string {
  102. taken := make(map[string]struct{}, len(used))
  103. for _, u := range used {
  104. taken[strings.TrimSpace(u)] = struct{}{}
  105. }
  106. for _, e := range entries {
  107. if _, ok := taken[e]; ok {
  108. return e
  109. }
  110. }
  111. return ""
  112. }
  113. // defaultWireguardClients fills in blank WireGuard credentials for newly added
  114. // clients: a generated keypair when none was provided, a derived public key when
  115. // only a private key was given, and a unique tunnel address allocated from the
  116. // inbound's subnet. It mutates both the typed clients and the parallel raw client
  117. // maps that get persisted into the inbound settings. Existing values are never
  118. // overwritten, so editing a client never rotates its keys.
  119. func defaultWireguardClients(existing, clients []model.Client, interfaceClients []any) error {
  120. used := make([]string, 0)
  121. for i := range existing {
  122. used = append(used, existing[i].AllowedIPs...)
  123. }
  124. base := wireguardAllocationBase(used, defaultWireguardBase)
  125. for i := range clients {
  126. c := &clients[i]
  127. if c.PrivateKey == "" && c.PublicKey == "" {
  128. priv, pub, err := wgutil.GenerateWireguardKeypair()
  129. if err != nil {
  130. return err
  131. }
  132. c.PrivateKey = priv
  133. c.PublicKey = pub
  134. } else if c.PublicKey == "" && c.PrivateKey != "" {
  135. pub, err := wgutil.PublicKeyFromPrivate(c.PrivateKey)
  136. if err != nil {
  137. return err
  138. }
  139. c.PublicKey = pub
  140. }
  141. if len(c.AllowedIPs) == 0 {
  142. addr, err := allocateWireguardAddress(used, base)
  143. if err != nil {
  144. return err
  145. }
  146. c.AllowedIPs = []string{addr}
  147. } else {
  148. normalized, err := normalizeWireguardAllowedIPs(c.AllowedIPs)
  149. if err != nil {
  150. return err
  151. }
  152. if len(normalized) == 0 {
  153. return common.NewError("wireguard: allowedIPs has no usable entry")
  154. }
  155. if hit := wireguardAllowedIPsCollision(normalized, used); hit != "" {
  156. return common.NewError("wireguard: allowedIPs entry already used by another client:", hit)
  157. }
  158. c.AllowedIPs = normalized
  159. }
  160. used = append(used, c.AllowedIPs...)
  161. if i < len(interfaceClients) {
  162. if m, ok := interfaceClients[i].(map[string]any); ok {
  163. m["privateKey"] = c.PrivateKey
  164. m["publicKey"] = c.PublicKey
  165. m["allowedIPs"] = c.AllowedIPs
  166. if c.PreSharedKey != "" {
  167. m["preSharedKey"] = c.PreSharedKey
  168. }
  169. if c.KeepAlive > 0 {
  170. m["keepAlive"] = c.KeepAlive
  171. }
  172. interfaceClients[i] = m
  173. }
  174. }
  175. }
  176. return nil
  177. }