analysis.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright 2023 The frp Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package nathole
  15. import (
  16. "cmp"
  17. "slices"
  18. "sync"
  19. "time"
  20. "github.com/samber/lo"
  21. )
  22. var (
  23. // mode 0, both EasyNAT, PublicNetwork is always receiver
  24. // sender | receiver, ttl 7
  25. // receiver, ttl 7 | sender
  26. // sender | receiver, ttl 4
  27. // receiver, ttl 4 | sender
  28. // sender | receiver
  29. // receiver | sender
  30. // sender, sendDelayMs 5000 | receiver
  31. // sender, sendDelayMs 10000 | receiver
  32. // receiver | sender, sendDelayMs 5000
  33. // receiver | sender, sendDelayMs 10000
  34. mode0Behaviors = []lo.Tuple2[RecommandBehavior, RecommandBehavior]{
  35. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 7}),
  36. lo.T2(RecommandBehavior{Role: DetectRoleReceiver, TTL: 7}, RecommandBehavior{Role: DetectRoleSender}),
  37. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 4}),
  38. lo.T2(RecommandBehavior{Role: DetectRoleReceiver, TTL: 4}, RecommandBehavior{Role: DetectRoleSender}),
  39. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver}),
  40. lo.T2(RecommandBehavior{Role: DetectRoleReceiver}, RecommandBehavior{Role: DetectRoleSender}),
  41. lo.T2(RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 5000}, RecommandBehavior{Role: DetectRoleReceiver}),
  42. lo.T2(RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 10000}, RecommandBehavior{Role: DetectRoleReceiver}),
  43. lo.T2(RecommandBehavior{Role: DetectRoleReceiver}, RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 5000}),
  44. lo.T2(RecommandBehavior{Role: DetectRoleReceiver}, RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 10000}),
  45. }
  46. // mode 1, HardNAT is sender, EasyNAT is receiver, port changes is regular
  47. // sender | receiver, ttl 7, portsRangeNumber max 10
  48. // sender, sendDelayMs 2000 | receiver, ttl 7, portsRangeNumber max 10
  49. // sender | receiver, ttl 4, portsRangeNumber max 10
  50. // sender, sendDelayMs 2000 | receiver, ttl 4, portsRangeNumber max 10
  51. // sender | receiver, portsRangeNumber max 10
  52. // sender, sendDelayMs 2000 | receiver, portsRangeNumber max 10
  53. mode1Behaviors = []lo.Tuple2[RecommandBehavior, RecommandBehavior]{
  54. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 7, PortsRangeNumber: 10}),
  55. lo.T2(RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 2000}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 7, PortsRangeNumber: 10}),
  56. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 4, PortsRangeNumber: 10}),
  57. lo.T2(RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 2000}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 4, PortsRangeNumber: 10}),
  58. lo.T2(RecommandBehavior{Role: DetectRoleSender}, RecommandBehavior{Role: DetectRoleReceiver, PortsRangeNumber: 10}),
  59. lo.T2(RecommandBehavior{Role: DetectRoleSender, SendDelayMs: 2000}, RecommandBehavior{Role: DetectRoleReceiver, PortsRangeNumber: 10}),
  60. }
  61. // mode 2, HardNAT is receiver, EasyNAT is sender
  62. // sender, portsRandomNumber 1000, sendDelayMs 3000 | receiver, listen 256 ports, ttl 7
  63. // sender, portsRandomNumber 1000, sendDelayMs 3000 | receiver, listen 256 ports, ttl 4
  64. // sender, portsRandomNumber 1000, sendDelayMs 3000 | receiver, listen 256 ports
  65. mode2Behaviors = []lo.Tuple2[RecommandBehavior, RecommandBehavior]{
  66. lo.T2(
  67. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  68. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256, TTL: 7},
  69. ),
  70. lo.T2(
  71. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  72. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256, TTL: 4},
  73. ),
  74. lo.T2(
  75. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  76. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256},
  77. ),
  78. }
  79. // mode 3, For HardNAT & HardNAT, both changes in the ports are regular
  80. // sender, portsRangeNumber 10 | receiver, ttl 7, portsRangeNumber 10
  81. // sender, portsRangeNumber 10 | receiver, ttl 4, portsRangeNumber 10
  82. // sender, portsRangeNumber 10 | receiver, portsRangeNumber 10
  83. // receiver, ttl 7, portsRangeNumber 10 | sender, portsRangeNumber 10
  84. // receiver, ttl 4, portsRangeNumber 10 | sender, portsRangeNumber 10
  85. // receiver, portsRangeNumber 10 | sender, portsRangeNumber 10
  86. mode3Behaviors = []lo.Tuple2[RecommandBehavior, RecommandBehavior]{
  87. lo.T2(RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 7, PortsRangeNumber: 10}),
  88. lo.T2(RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleReceiver, TTL: 4, PortsRangeNumber: 10}),
  89. lo.T2(RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleReceiver, PortsRangeNumber: 10}),
  90. lo.T2(RecommandBehavior{Role: DetectRoleReceiver, TTL: 7, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}),
  91. lo.T2(RecommandBehavior{Role: DetectRoleReceiver, TTL: 4, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}),
  92. lo.T2(RecommandBehavior{Role: DetectRoleReceiver, PortsRangeNumber: 10}, RecommandBehavior{Role: DetectRoleSender, PortsRangeNumber: 10}),
  93. }
  94. // mode 4, Regular ports changes are usually the sender.
  95. // sender, portsRandomNumber 1000, sendDelayMs: 2000 | receiver, listen 256 ports, ttl 7, portsRangeNumber 2
  96. // sender, portsRandomNumber 1000, sendDelayMs: 2000 | receiver, listen 256 ports, ttl 4, portsRangeNumber 2
  97. // sender, portsRandomNumber 1000, SendDelayMs: 2000 | receiver, listen 256 ports, portsRangeNumber 2
  98. mode4Behaviors = []lo.Tuple2[RecommandBehavior, RecommandBehavior]{
  99. lo.T2(
  100. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  101. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256, TTL: 7, PortsRangeNumber: 2},
  102. ),
  103. lo.T2(
  104. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  105. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256, TTL: 4, PortsRangeNumber: 2},
  106. ),
  107. lo.T2(
  108. RecommandBehavior{Role: DetectRoleSender, PortsRandomNumber: 1000, SendDelayMs: 3000},
  109. RecommandBehavior{Role: DetectRoleReceiver, ListenRandomPorts: 256, PortsRangeNumber: 2},
  110. ),
  111. }
  112. )
  113. func getBehaviorByMode(mode int) []lo.Tuple2[RecommandBehavior, RecommandBehavior] {
  114. switch mode {
  115. case 0:
  116. return mode0Behaviors
  117. case 1:
  118. return mode1Behaviors
  119. case 2:
  120. return mode2Behaviors
  121. case 3:
  122. return mode3Behaviors
  123. case 4:
  124. return mode4Behaviors
  125. }
  126. // default
  127. return mode0Behaviors
  128. }
  129. func getBehaviorByModeAndIndex(mode int, index int) (RecommandBehavior, RecommandBehavior) {
  130. behaviors := getBehaviorByMode(mode)
  131. if index >= len(behaviors) {
  132. return RecommandBehavior{}, RecommandBehavior{}
  133. }
  134. return behaviors[index].A, behaviors[index].B
  135. }
  136. func getBehaviorScoresByMode(mode int, defaultScore int) []*BehaviorScore {
  137. return getBehaviorScoresByMode2(mode, defaultScore, defaultScore)
  138. }
  139. func getBehaviorScoresByMode2(mode int, senderScore, receiverScore int) []*BehaviorScore {
  140. behaviors := getBehaviorByMode(mode)
  141. scores := make([]*BehaviorScore, 0, len(behaviors))
  142. for i := 0; i < len(behaviors); i++ {
  143. score := receiverScore
  144. if behaviors[i].A.Role == DetectRoleSender {
  145. score = senderScore
  146. }
  147. scores = append(scores, &BehaviorScore{Mode: mode, Index: i, Score: score})
  148. }
  149. return scores
  150. }
  151. type RecommandBehavior struct {
  152. Role string
  153. TTL int
  154. SendDelayMs int
  155. PortsRangeNumber int
  156. PortsRandomNumber int
  157. ListenRandomPorts int
  158. }
  159. type MakeHoleRecords struct {
  160. mu sync.Mutex
  161. scores []*BehaviorScore
  162. LastUpdateTime time.Time
  163. }
  164. func NewMakeHoleRecords(c, v *NatFeature) *MakeHoleRecords {
  165. scores := []*BehaviorScore{}
  166. easyCount, hardCount, portsChangedRegularCount := ClassifyFeatureCount([]*NatFeature{c, v})
  167. appendMode0 := func() {
  168. switch {
  169. case c.PublicNetwork:
  170. scores = append(scores, getBehaviorScoresByMode2(DetectMode0, 0, 1)...)
  171. case v.PublicNetwork:
  172. scores = append(scores, getBehaviorScoresByMode2(DetectMode0, 1, 0)...)
  173. default:
  174. scores = append(scores, getBehaviorScoresByMode(DetectMode0, 0)...)
  175. }
  176. }
  177. switch {
  178. case easyCount == 2:
  179. appendMode0()
  180. case hardCount == 1 && portsChangedRegularCount == 1:
  181. scores = append(scores, getBehaviorScoresByMode(DetectMode1, 0)...)
  182. scores = append(scores, getBehaviorScoresByMode(DetectMode2, 0)...)
  183. appendMode0()
  184. case hardCount == 1 && portsChangedRegularCount == 0:
  185. scores = append(scores, getBehaviorScoresByMode(DetectMode2, 0)...)
  186. scores = append(scores, getBehaviorScoresByMode(DetectMode1, 0)...)
  187. appendMode0()
  188. case hardCount == 2 && portsChangedRegularCount == 2:
  189. scores = append(scores, getBehaviorScoresByMode(DetectMode3, 0)...)
  190. scores = append(scores, getBehaviorScoresByMode(DetectMode4, 0)...)
  191. case hardCount == 2 && portsChangedRegularCount == 1:
  192. scores = append(scores, getBehaviorScoresByMode(DetectMode4, 0)...)
  193. default:
  194. // hard to make hole, just trying it out.
  195. scores = append(scores, getBehaviorScoresByMode(DetectMode0, 1)...)
  196. scores = append(scores, getBehaviorScoresByMode(DetectMode1, 1)...)
  197. scores = append(scores, getBehaviorScoresByMode(DetectMode3, 1)...)
  198. }
  199. return &MakeHoleRecords{scores: scores, LastUpdateTime: time.Now()}
  200. }
  201. func (mhr *MakeHoleRecords) ReportSuccess(mode int, index int) {
  202. mhr.mu.Lock()
  203. defer mhr.mu.Unlock()
  204. mhr.LastUpdateTime = time.Now()
  205. for i := range mhr.scores {
  206. score := mhr.scores[i]
  207. if score.Mode != mode || score.Index != index {
  208. continue
  209. }
  210. score.Score += 2
  211. score.Score = min(score.Score, 10)
  212. return
  213. }
  214. }
  215. func (mhr *MakeHoleRecords) Recommand() (mode, index int) {
  216. mhr.mu.Lock()
  217. defer mhr.mu.Unlock()
  218. if len(mhr.scores) == 0 {
  219. return 0, 0
  220. }
  221. maxScore := slices.MaxFunc(mhr.scores, func(a, b *BehaviorScore) int {
  222. return cmp.Compare(a.Score, b.Score)
  223. })
  224. maxScore.Score--
  225. mhr.LastUpdateTime = time.Now()
  226. return maxScore.Mode, maxScore.Index
  227. }
  228. type BehaviorScore struct {
  229. Mode int
  230. Index int
  231. // between -10 and 10
  232. Score int
  233. }
  234. type Analyzer struct {
  235. // key is client ip + visitor ip
  236. records map[string]*MakeHoleRecords
  237. dataReserveDuration time.Duration
  238. mu sync.Mutex
  239. }
  240. func NewAnalyzer(dataReserveDuration time.Duration) *Analyzer {
  241. return &Analyzer{
  242. records: make(map[string]*MakeHoleRecords),
  243. dataReserveDuration: dataReserveDuration,
  244. }
  245. }
  246. func (a *Analyzer) GetRecommandBehaviors(key string, c, v *NatFeature) (mode, index int, _ RecommandBehavior, _ RecommandBehavior) {
  247. a.mu.Lock()
  248. records, ok := a.records[key]
  249. if !ok {
  250. records = NewMakeHoleRecords(c, v)
  251. a.records[key] = records
  252. }
  253. a.mu.Unlock()
  254. mode, index = records.Recommand()
  255. cBehavior, vBehavior := getBehaviorByModeAndIndex(mode, index)
  256. switch mode {
  257. case DetectMode1:
  258. // HardNAT is always the sender
  259. if c.NatType == EasyNAT {
  260. cBehavior, vBehavior = vBehavior, cBehavior
  261. }
  262. case DetectMode2:
  263. // HardNAT is always the receiver
  264. if c.NatType == HardNAT {
  265. cBehavior, vBehavior = vBehavior, cBehavior
  266. }
  267. case DetectMode4:
  268. // Regular ports changes is always the sender
  269. if !c.RegularPortsChange {
  270. cBehavior, vBehavior = vBehavior, cBehavior
  271. }
  272. }
  273. return mode, index, cBehavior, vBehavior
  274. }
  275. func (a *Analyzer) ReportSuccess(key string, mode, index int) {
  276. a.mu.Lock()
  277. records, ok := a.records[key]
  278. a.mu.Unlock()
  279. if !ok {
  280. return
  281. }
  282. records.ReportSuccess(mode, index)
  283. }
  284. func (a *Analyzer) Clean() (int, int) {
  285. now := time.Now()
  286. total := 0
  287. count := 0
  288. // cleanup 10w records may take 5ms
  289. a.mu.Lock()
  290. defer a.mu.Unlock()
  291. total = len(a.records)
  292. // clean up records that have not been used for a period of time.
  293. for key, records := range a.records {
  294. if now.Sub(records.LastUpdateTime) > a.dataReserveDuration {
  295. delete(a.records, key)
  296. count++
  297. }
  298. }
  299. return count, total
  300. }