trafficRules_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. * Copyright (c) 2022, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package server
  20. import (
  21. "encoding/json"
  22. "io/ioutil"
  23. "os"
  24. "reflect"
  25. "testing"
  26. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  27. )
  28. func TestTrafficRulesFilters(t *testing.T) {
  29. trafficRulesJSON := `
  30. {
  31. "DefaultRules" : {
  32. "RateLimits" : {
  33. "WriteUnthrottledBytes": 1,
  34. "WriteBytesPerSecond": 2,
  35. "ReadUnthrottledBytes": 3,
  36. "ReadBytesPerSecond": 4,
  37. "UnthrottleFirstTunnelOnly": true
  38. },
  39. "AllowTCPPorts" : [5],
  40. "AllowUDPPorts" : [6]
  41. },
  42. "FilteredRules" : [
  43. {
  44. "Filter" : {
  45. "ProviderIDs" : ["H2"]
  46. },
  47. "Rules" : {
  48. "RateLimits" : {
  49. "WriteBytesPerSecond": 99,
  50. "ReadBytesPerSecond": 99
  51. }
  52. }
  53. },
  54. {
  55. "Filter" : {
  56. "ProviderIDs" : ["H1"],
  57. "Regions" : ["R2"],
  58. "HandshakeParameters" : {
  59. "client_version" : ["1"]
  60. }
  61. },
  62. "Rules" : {
  63. "RateLimits" : {
  64. "WriteBytesPerSecond": 7,
  65. "ReadBytesPerSecond": 8
  66. },
  67. "AllowTCPPorts" : [5,9],
  68. "AllowUDPPorts" : [6,10]
  69. }
  70. },
  71. {
  72. "Filter" : {
  73. "TunnelProtocols" : ["P2"],
  74. "Regions" : ["R3", "R4"],
  75. "HandshakeParameters" : {
  76. "client_version" : ["1", "2"]
  77. }
  78. },
  79. "ExceptFilter" : {
  80. "ISPs" : ["I2", "I3"],
  81. "HandshakeParameters" : {
  82. "client_version" : ["1"]
  83. }
  84. },
  85. "Rules" : {
  86. "RateLimits" : {
  87. "WriteBytesPerSecond": 11,
  88. "ReadBytesPerSecond": 12
  89. },
  90. "AllowTCPPorts" : [5,13],
  91. "AllowUDPPorts" : [6,14]
  92. }
  93. },
  94. {
  95. "Filter" : {
  96. "Regions" : ["R3", "R4"],
  97. "HandshakeParameters" : {
  98. "client_version" : ["1", "2"]
  99. }
  100. },
  101. "ExceptFilter" : {
  102. "ISPs" : ["I2", "I3"],
  103. "HandshakeParameters" : {
  104. "client_version" : ["1"]
  105. }
  106. },
  107. "Rules" : {
  108. "RateLimits" : {
  109. "WriteBytesPerSecond": 15,
  110. "ReadBytesPerSecond": 16
  111. },
  112. "AllowTCPPorts" : [5,17],
  113. "AllowUDPPorts" : [6,18]
  114. }
  115. },
  116. {
  117. "Filter" : {
  118. "Regions" : ["R5"],
  119. "MinClientVersion" : 30,
  120. "MaxClientVersion" : 40
  121. },
  122. "Rules" : {
  123. "RateLimits" : {
  124. "WriteBytesPerSecond": 17,
  125. "ReadBytesPerSecond": 18
  126. },
  127. "AllowTCPPorts" : [5,9],
  128. "AllowUDPPorts" : [6,10]
  129. }
  130. },
  131. {
  132. "Filter" : {
  133. "Regions" : ["R5"],
  134. "MinClientVersion" : 10,
  135. "MaxClientVersion" : 20
  136. },
  137. "Rules" : {
  138. "RateLimits" : {
  139. "WriteBytesPerSecond": 19,
  140. "ReadBytesPerSecond": 20
  141. },
  142. "AllowTCPPorts" : [7,11],
  143. "AllowUDPPorts" : [8,12]
  144. }
  145. }
  146. ]
  147. }
  148. `
  149. file, err := ioutil.TempFile("", "trafficRules.config")
  150. if err != nil {
  151. t.Fatalf("TempFile create failed: %s", err)
  152. }
  153. _, err = file.Write([]byte(trafficRulesJSON))
  154. if err != nil {
  155. t.Fatalf("TempFile write failed: %s", err)
  156. }
  157. file.Close()
  158. configFileName := file.Name()
  159. defer os.Remove(configFileName)
  160. trafficRules, err := NewTrafficRulesSet(configFileName)
  161. if err != nil {
  162. t.Fatalf("NewTrafficRulesSet failed: %s", err)
  163. }
  164. err = trafficRules.Validate()
  165. if err != nil {
  166. t.Fatalf("TrafficRulesSet.Validate failed: %s", err)
  167. }
  168. makePortList := func(portsJSON string) common.PortList {
  169. var p common.PortList
  170. _ = json.Unmarshal([]byte(portsJSON), &p)
  171. return p
  172. }
  173. // should never get 1st filtered rule with different provider ID
  174. providerID := "H1"
  175. testCases := []struct {
  176. description string
  177. providerID string
  178. isFirstTunnelInSession bool
  179. tunnelProtocol string
  180. geoIPData GeoIPData
  181. state handshakeState
  182. expectedWriteUnthrottledBytes int64
  183. expectedWriteBytesPerSecond int64
  184. expectedReadUnthrottledBytes int64
  185. expectedReadBytesPerSecond int64
  186. expectedAllowTCPPorts common.PortList
  187. expectedAllowUDPPorts common.PortList
  188. }{
  189. {
  190. "get defaults",
  191. providerID,
  192. true,
  193. "P1",
  194. GeoIPData{Country: "R1", ISP: "I1"},
  195. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  196. 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
  197. },
  198. {
  199. "get defaults for not first tunnel in session",
  200. providerID,
  201. false,
  202. "P1",
  203. GeoIPData{Country: "R1", ISP: "I1"},
  204. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  205. 0, 2, 0, 4, makePortList("[5]"), makePortList("[6]"),
  206. },
  207. {
  208. "get 2nd filtered rule (including provider ID)",
  209. providerID,
  210. true,
  211. "P1",
  212. GeoIPData{Country: "R2", ISP: "I1"},
  213. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  214. 1, 7, 3, 8, makePortList("[5,9]"), makePortList("[6,10]"),
  215. },
  216. {
  217. "don't get 2nd filtered rule with incomplete match",
  218. providerID,
  219. true,
  220. "P1",
  221. GeoIPData{Country: "R2", ISP: "I1"},
  222. handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
  223. 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
  224. },
  225. {
  226. "get 3rd filtered rule",
  227. providerID,
  228. true,
  229. "P2",
  230. GeoIPData{Country: "R3", ISP: "I1"},
  231. handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
  232. 1, 11, 3, 12, makePortList("[5,13]"), makePortList("[6,14]"),
  233. },
  234. {
  235. "get 3rd filtered rule with incomplete exception",
  236. providerID,
  237. true,
  238. "P2",
  239. GeoIPData{Country: "R3", ISP: "I2"},
  240. handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
  241. 1, 11, 3, 12, makePortList("[5,13]"), makePortList("[6,14]"),
  242. },
  243. {
  244. "don't get 3rd filtered rule due to exception",
  245. providerID,
  246. true,
  247. "P2",
  248. GeoIPData{Country: "R3", ISP: "I2"},
  249. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  250. 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
  251. },
  252. {
  253. "get 4th filtered rule",
  254. providerID,
  255. true,
  256. "P1",
  257. GeoIPData{Country: "R3", ISP: "I1"},
  258. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  259. 1, 15, 3, 16, makePortList("[5,17]"), makePortList("[6,18]"),
  260. },
  261. {
  262. "don't get 4th filtered rule due to exception",
  263. providerID,
  264. true,
  265. "P1",
  266. GeoIPData{Country: "R3", ISP: "I2"},
  267. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  268. 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
  269. },
  270. {
  271. "don't get 4th filtered rule due to Min/MaxClientVersion",
  272. providerID,
  273. true,
  274. "P1",
  275. GeoIPData{Country: "R3", ISP: "I2"},
  276. handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
  277. 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
  278. },
  279. {
  280. "match 2nd Min/MaxClientVersion filtered rule",
  281. providerID,
  282. true,
  283. "P1",
  284. GeoIPData{Country: "R5", ISP: "I1"},
  285. handshakeState{apiParams: map[string]interface{}{"client_version": "15"}, completed: true},
  286. 1, 19, 3, 20, makePortList("[7,11]"), makePortList("[8,12]"),
  287. },
  288. }
  289. for _, testCase := range testCases {
  290. t.Run(testCase.description, func(t *testing.T) {
  291. rules := trafficRules.GetTrafficRules(
  292. testCase.providerID,
  293. testCase.isFirstTunnelInSession,
  294. testCase.tunnelProtocol,
  295. testCase.geoIPData,
  296. testCase.state)
  297. if *rules.RateLimits.WriteUnthrottledBytes != testCase.expectedWriteUnthrottledBytes {
  298. t.Errorf("unexpected rules.RateLimits.WriteUnthrottledBytes: %v != %v",
  299. *rules.RateLimits.WriteUnthrottledBytes, testCase.expectedWriteUnthrottledBytes)
  300. }
  301. if *rules.RateLimits.WriteBytesPerSecond != testCase.expectedWriteBytesPerSecond {
  302. t.Errorf("unexpected rules.RateLimits.WriteBytesPerSecond: %v != %v",
  303. *rules.RateLimits.WriteBytesPerSecond, testCase.expectedWriteBytesPerSecond)
  304. }
  305. if *rules.RateLimits.ReadUnthrottledBytes != testCase.expectedReadUnthrottledBytes {
  306. t.Errorf("unexpected rules.RateLimits.ReadUnthrottledBytes: %v != %v",
  307. *rules.RateLimits.ReadUnthrottledBytes, testCase.expectedReadUnthrottledBytes)
  308. }
  309. if *rules.RateLimits.ReadBytesPerSecond != testCase.expectedReadBytesPerSecond {
  310. t.Errorf("unexpected rules.RateLimits.ReadBytesPerSecond: %v != %v",
  311. *rules.RateLimits.ReadBytesPerSecond, testCase.expectedReadBytesPerSecond)
  312. }
  313. if !reflect.DeepEqual(*rules.AllowTCPPorts, testCase.expectedAllowTCPPorts) {
  314. t.Errorf("unexpected rules.RateLimits.AllowTCPPorts: %v != %v",
  315. *rules.AllowTCPPorts, testCase.expectedAllowTCPPorts)
  316. }
  317. if !reflect.DeepEqual(*rules.AllowUDPPorts, testCase.expectedAllowUDPPorts) {
  318. t.Errorf("unexpected rules.RateLimits.AllowUDPPorts: %v != %v",
  319. *rules.AllowUDPPorts, testCase.expectedAllowUDPPorts)
  320. }
  321. })
  322. }
  323. }