analysis.go 12 KB

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