server_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*
  2. * Copyright (c) 2016, 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. "errors"
  23. "flag"
  24. "fmt"
  25. "io/ioutil"
  26. "net"
  27. "net/http"
  28. "net/url"
  29. "os"
  30. "strconv"
  31. "sync"
  32. "syscall"
  33. "testing"
  34. "time"
  35. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
  36. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  37. "golang.org/x/net/proxy"
  38. )
  39. func TestMain(m *testing.M) {
  40. flag.Parse()
  41. os.Remove(psiphon.DATA_STORE_FILENAME)
  42. psiphon.SetEmitDiagnosticNotices(true)
  43. os.Exit(m.Run())
  44. }
  45. // Note: not testing fronting meek protocols, which client is
  46. // hard-wired to except running on privileged ports 80 and 443.
  47. func TestSSH(t *testing.T) {
  48. runServer(t,
  49. &runServerConfig{
  50. tunnelProtocol: "SSH",
  51. enableSSHAPIRequests: true,
  52. doHotReload: false,
  53. denyTrafficRules: false,
  54. })
  55. }
  56. func TestOSSH(t *testing.T) {
  57. runServer(t,
  58. &runServerConfig{
  59. tunnelProtocol: "OSSH",
  60. enableSSHAPIRequests: true,
  61. doHotReload: false,
  62. denyTrafficRules: false,
  63. })
  64. }
  65. func TestUnfrontedMeek(t *testing.T) {
  66. runServer(t,
  67. &runServerConfig{
  68. tunnelProtocol: "UNFRONTED-MEEK-OSSH",
  69. enableSSHAPIRequests: true,
  70. doHotReload: false,
  71. denyTrafficRules: false,
  72. })
  73. }
  74. func TestUnfrontedMeekHTTPS(t *testing.T) {
  75. runServer(t,
  76. &runServerConfig{
  77. tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH",
  78. enableSSHAPIRequests: true,
  79. doHotReload: false,
  80. denyTrafficRules: false,
  81. })
  82. }
  83. func TestWebTransportAPIRequests(t *testing.T) {
  84. runServer(t,
  85. &runServerConfig{
  86. tunnelProtocol: "OSSH",
  87. enableSSHAPIRequests: false,
  88. doHotReload: false,
  89. denyTrafficRules: false,
  90. })
  91. }
  92. func TestHotReload(t *testing.T) {
  93. runServer(t,
  94. &runServerConfig{
  95. tunnelProtocol: "OSSH",
  96. enableSSHAPIRequests: true,
  97. doHotReload: true,
  98. denyTrafficRules: false,
  99. })
  100. }
  101. func TestDenyTrafficRules(t *testing.T) {
  102. runServer(t,
  103. &runServerConfig{
  104. tunnelProtocol: "OSSH",
  105. enableSSHAPIRequests: true,
  106. doHotReload: true,
  107. denyTrafficRules: true,
  108. })
  109. }
  110. type runServerConfig struct {
  111. tunnelProtocol string
  112. enableSSHAPIRequests bool
  113. doHotReload bool
  114. denyTrafficRules bool
  115. }
  116. func sendNotificationReceived(c chan<- struct{}) {
  117. select {
  118. case c <- *new(struct{}):
  119. default:
  120. }
  121. }
  122. func waitOnNotification(t *testing.T, c, timeoutSignal <-chan struct{}, timeoutMessage string) {
  123. select {
  124. case <-c:
  125. case <-timeoutSignal:
  126. t.Fatalf(timeoutMessage)
  127. }
  128. }
  129. const dummyClientVerificationPayload = `
  130. {
  131. "status": 0,
  132. "payload": ""
  133. }`
  134. func runServer(t *testing.T, runConfig *runServerConfig) {
  135. // create a server
  136. var err error
  137. serverIPaddress := ""
  138. for _, interfaceName := range []string{"eth0", "en0"} {
  139. serverIPaddress, err = psiphon.GetInterfaceIPAddress(interfaceName)
  140. if err == nil {
  141. break
  142. }
  143. }
  144. if err != nil {
  145. t.Fatalf("error getting server IP address: %s", err)
  146. }
  147. serverConfigJSON, _, encodedServerEntry, err := GenerateConfig(
  148. &GenerateConfigParams{
  149. ServerIPAddress: serverIPaddress,
  150. EnableSSHAPIRequests: runConfig.enableSSHAPIRequests,
  151. WebServerPort: 8000,
  152. TunnelProtocolPorts: map[string]int{runConfig.tunnelProtocol: 4000},
  153. })
  154. if err != nil {
  155. t.Fatalf("error generating server config: %s", err)
  156. }
  157. // customize server config
  158. // Pave psinet with random values to test handshake homepages.
  159. psinetFilename := "psinet.json"
  160. sponsorID, expectedHomepageURL := pavePsinetDatabaseFile(t, psinetFilename)
  161. // Pave traffic rules file which exercises handshake parameter filtering
  162. trafficRulesFilename := "traffic_rules.json"
  163. paveTrafficRulesFile(t, trafficRulesFilename, sponsorID, runConfig.denyTrafficRules)
  164. var serverConfig interface{}
  165. json.Unmarshal(serverConfigJSON, &serverConfig)
  166. serverConfig.(map[string]interface{})["GeoIPDatabaseFilename"] = ""
  167. serverConfig.(map[string]interface{})["PsinetDatabaseFilename"] = psinetFilename
  168. serverConfig.(map[string]interface{})["TrafficRulesFilename"] = trafficRulesFilename
  169. serverConfig.(map[string]interface{})["LogLevel"] = "debug"
  170. // 1 second is the minimum period; should be small enough to emit a log during the
  171. // test run, but not guaranteed
  172. serverConfig.(map[string]interface{})["LoadMonitorPeriodSeconds"] = 1
  173. serverConfigJSON, _ = json.Marshal(serverConfig)
  174. // run server
  175. serverWaitGroup := new(sync.WaitGroup)
  176. serverWaitGroup.Add(1)
  177. go func() {
  178. defer serverWaitGroup.Done()
  179. err := RunServices(serverConfigJSON)
  180. if err != nil {
  181. // TODO: wrong goroutine for t.FatalNow()
  182. t.Fatalf("error running server: %s", err)
  183. }
  184. }()
  185. defer func() {
  186. // Test: orderly server shutdown
  187. p, _ := os.FindProcess(os.Getpid())
  188. p.Signal(os.Interrupt)
  189. shutdownTimeout := time.NewTimer(5 * time.Second)
  190. shutdownOk := make(chan struct{}, 1)
  191. go func() {
  192. serverWaitGroup.Wait()
  193. shutdownOk <- *new(struct{})
  194. }()
  195. select {
  196. case <-shutdownOk:
  197. case <-shutdownTimeout.C:
  198. t.Fatalf("server shutdown timeout exceeded")
  199. }
  200. }()
  201. // Test: hot reload (of psinet)
  202. if runConfig.doHotReload {
  203. // TODO: monitor logs for more robust wait-until-loaded
  204. time.Sleep(1 * time.Second)
  205. // Pave a new psinet with different random values.
  206. sponsorID, expectedHomepageURL = pavePsinetDatabaseFile(t, psinetFilename)
  207. p, _ := os.FindProcess(os.Getpid())
  208. p.Signal(syscall.SIGUSR1)
  209. // TODO: monitor logs for more robust wait-until-reloaded
  210. time.Sleep(1 * time.Second)
  211. // After reloading psinet, the new sponsorID/expectedHomepageURL
  212. // should be active, as tested in the client "Homepage" notice
  213. // handler below.
  214. }
  215. // connect to server with client
  216. // TODO: currently, TargetServerEntry only works with one tunnel
  217. numTunnels := 1
  218. localSOCKSProxyPort := 1081
  219. localHTTPProxyPort := 8081
  220. establishTunnelPausePeriodSeconds := 1
  221. // Note: calling LoadConfig ensures all *int config fields are initialized
  222. clientConfigJSON := `
  223. {
  224. "ClientPlatform" : "Android",
  225. "ClientVersion" : "0",
  226. "SponsorId" : "0",
  227. "PropagationChannelId" : "0"
  228. }`
  229. clientConfig, _ := psiphon.LoadConfig([]byte(clientConfigJSON))
  230. clientConfig.SponsorId = sponsorID
  231. clientConfig.ConnectionWorkerPoolSize = numTunnels
  232. clientConfig.TunnelPoolSize = numTunnels
  233. clientConfig.DisableRemoteServerListFetcher = true
  234. clientConfig.EstablishTunnelPausePeriodSeconds = &establishTunnelPausePeriodSeconds
  235. clientConfig.TargetServerEntry = string(encodedServerEntry)
  236. clientConfig.TunnelProtocol = runConfig.tunnelProtocol
  237. clientConfig.LocalSocksProxyPort = localSOCKSProxyPort
  238. clientConfig.LocalHttpProxyPort = localHTTPProxyPort
  239. err = psiphon.InitDataStore(clientConfig)
  240. if err != nil {
  241. t.Fatalf("error initializing client datastore: %s", err)
  242. }
  243. controller, err := psiphon.NewController(clientConfig)
  244. if err != nil {
  245. t.Fatalf("error creating client controller: %s", err)
  246. }
  247. tunnelsEstablished := make(chan struct{}, 1)
  248. homepageReceived := make(chan struct{}, 1)
  249. verificationRequired := make(chan struct{}, 1)
  250. verificationCompleted := make(chan struct{}, 1)
  251. psiphon.SetNoticeOutput(psiphon.NewNoticeReceiver(
  252. func(notice []byte) {
  253. //fmt.Printf("%s\n", string(notice))
  254. noticeType, payload, err := psiphon.GetNotice(notice)
  255. if err != nil {
  256. return
  257. }
  258. switch noticeType {
  259. case "Tunnels":
  260. // Do not set verification payload until tunnel is
  261. // established. Otherwise will silently take no action.
  262. controller.SetClientVerificationPayloadForActiveTunnels("")
  263. count := int(payload["count"].(float64))
  264. if count >= numTunnels {
  265. sendNotificationReceived(tunnelsEstablished)
  266. }
  267. case "Homepage":
  268. homepageURL := payload["url"].(string)
  269. if homepageURL != expectedHomepageURL {
  270. // TODO: wrong goroutine for t.FatalNow()
  271. t.Fatalf("unexpected homepage: %s", homepageURL)
  272. }
  273. sendNotificationReceived(homepageReceived)
  274. case "ClientVerificationRequired":
  275. sendNotificationReceived(verificationRequired)
  276. controller.SetClientVerificationPayloadForActiveTunnels(dummyClientVerificationPayload)
  277. case "NoticeClientVerificationRequestCompleted":
  278. sendNotificationReceived(verificationCompleted)
  279. }
  280. }))
  281. controllerShutdownBroadcast := make(chan struct{})
  282. controllerWaitGroup := new(sync.WaitGroup)
  283. controllerWaitGroup.Add(1)
  284. go func() {
  285. defer controllerWaitGroup.Done()
  286. controller.Run(controllerShutdownBroadcast)
  287. }()
  288. defer func() {
  289. close(controllerShutdownBroadcast)
  290. shutdownTimeout := time.NewTimer(20 * time.Second)
  291. shutdownOk := make(chan struct{}, 1)
  292. go func() {
  293. controllerWaitGroup.Wait()
  294. shutdownOk <- *new(struct{})
  295. }()
  296. select {
  297. case <-shutdownOk:
  298. case <-shutdownTimeout.C:
  299. t.Fatalf("controller shutdown timeout exceeded")
  300. }
  301. }()
  302. // Test: tunnels must be established, and correct homepage
  303. // must be received, within 30 seconds
  304. timeoutSignal := make(chan struct{})
  305. go func() {
  306. timer := time.NewTimer(30 * time.Second)
  307. <-timer.C
  308. close(timeoutSignal)
  309. }()
  310. waitOnNotification(t, tunnelsEstablished, timeoutSignal, "tunnel establish timeout exceeded")
  311. waitOnNotification(t, homepageReceived, timeoutSignal, "homepage received timeout exceeded")
  312. waitOnNotification(t, verificationRequired, timeoutSignal, "verification required timeout exceeded")
  313. waitOnNotification(t, verificationCompleted, timeoutSignal, "verification completed timeout exceeded")
  314. // Test: tunneled web site fetch
  315. err = makeTunneledWebRequest(t, localHTTPProxyPort)
  316. if err == nil {
  317. if runConfig.denyTrafficRules {
  318. t.Fatalf("unexpected tunneled web request success")
  319. }
  320. } else {
  321. if !runConfig.denyTrafficRules {
  322. t.Fatalf("tunneled web request failed: %s", err)
  323. }
  324. }
  325. // Test: tunneled UDP packets
  326. udpgwServerAddress := serverConfig.(map[string]interface{})["UDPInterceptUdpgwServerAddress"].(string)
  327. err = makeTunneledNTPRequest(t, localSOCKSProxyPort, udpgwServerAddress)
  328. if err == nil {
  329. if runConfig.denyTrafficRules {
  330. t.Fatalf("unexpected tunneled NTP request success")
  331. }
  332. } else {
  333. if !runConfig.denyTrafficRules {
  334. t.Fatalf("tunneled NTP request failed: %s", err)
  335. }
  336. }
  337. }
  338. func makeTunneledWebRequest(t *testing.T, localHTTPProxyPort int) error {
  339. testUrl := "https://psiphon.ca"
  340. roundTripTimeout := 30 * time.Second
  341. proxyUrl, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", localHTTPProxyPort))
  342. if err != nil {
  343. return fmt.Errorf("error initializing proxied HTTP request: %s", err)
  344. }
  345. httpClient := &http.Client{
  346. Transport: &http.Transport{
  347. Proxy: http.ProxyURL(proxyUrl),
  348. },
  349. Timeout: roundTripTimeout,
  350. }
  351. response, err := httpClient.Get(testUrl)
  352. if err != nil {
  353. return fmt.Errorf("error sending proxied HTTP request: %s", err)
  354. }
  355. _, err = ioutil.ReadAll(response.Body)
  356. if err != nil {
  357. return fmt.Errorf("error reading proxied HTTP response: %s", err)
  358. }
  359. response.Body.Close()
  360. return nil
  361. }
  362. func makeTunneledNTPRequest(t *testing.T, localSOCKSProxyPort int, udpgwServerAddress string) error {
  363. testHostname := "pool.ntp.org"
  364. timeout := 10 * time.Second
  365. localUDPProxyAddress, err := net.ResolveUDPAddr("udp", "127.0.0.1:7301")
  366. if err != nil {
  367. t.Fatalf("ResolveUDPAddr failed: %s", err)
  368. }
  369. // Note: this proxy is intended for this test only -- it only accepts a single connection,
  370. // handles it, and then terminates.
  371. localUDPProxy := func(destinationIP net.IP, destinationPort uint16, waitGroup *sync.WaitGroup) {
  372. if waitGroup != nil {
  373. defer waitGroup.Done()
  374. }
  375. destination := net.JoinHostPort(destinationIP.String(), strconv.Itoa(int(destinationPort)))
  376. serverUDPConn, err := net.ListenUDP("udp", localUDPProxyAddress)
  377. if err != nil {
  378. t.Logf("ListenUDP for %s failed: %s", destination, err)
  379. return
  380. }
  381. defer serverUDPConn.Close()
  382. udpgwPreambleSize := 11 // see writeUdpgwPreamble
  383. buffer := make([]byte, udpgwProtocolMaxMessageSize)
  384. packetSize, clientAddr, err := serverUDPConn.ReadFromUDP(
  385. buffer[udpgwPreambleSize:len(buffer)])
  386. if err != nil {
  387. t.Logf("serverUDPConn.Read for %s failed: %s", destination, err)
  388. return
  389. }
  390. socksProxyAddress := fmt.Sprintf("127.0.0.1:%d", localSOCKSProxyPort)
  391. dialer, err := proxy.SOCKS5("tcp", socksProxyAddress, nil, proxy.Direct)
  392. if err != nil {
  393. t.Logf("proxy.SOCKS5 for %s failed: %s", destination, err)
  394. return
  395. }
  396. socksTCPConn, err := dialer.Dial("tcp", udpgwServerAddress)
  397. if err != nil {
  398. t.Logf("dialer.Dial for %s failed: %s", destination, err)
  399. return
  400. }
  401. defer socksTCPConn.Close()
  402. flags := uint8(0)
  403. if destinationPort == 53 {
  404. flags = udpgwProtocolFlagDNS
  405. }
  406. err = writeUdpgwPreamble(
  407. udpgwPreambleSize,
  408. flags,
  409. 0,
  410. destinationIP,
  411. destinationPort,
  412. uint16(packetSize),
  413. buffer)
  414. if err != nil {
  415. t.Logf("writeUdpgwPreamble for %s failed: %s", destination, err)
  416. return
  417. }
  418. _, err = socksTCPConn.Write(buffer[0 : udpgwPreambleSize+packetSize])
  419. if err != nil {
  420. t.Logf("socksTCPConn.Write for %s failed: %s", destination, err)
  421. return
  422. }
  423. updgwProtocolMessage, err := readUdpgwMessage(socksTCPConn, buffer)
  424. if err != nil {
  425. t.Logf("readUdpgwMessage for %s failed: %s", destination, err)
  426. return
  427. }
  428. _, err = serverUDPConn.WriteToUDP(updgwProtocolMessage.packet, clientAddr)
  429. if err != nil {
  430. t.Logf("serverUDPConn.Write for %s failed: %s", destination, err)
  431. return
  432. }
  433. }
  434. // Tunneled DNS request
  435. waitGroup := new(sync.WaitGroup)
  436. waitGroup.Add(1)
  437. go localUDPProxy(
  438. net.IP(make([]byte, 4)), // ignored due to transparent DNS forwarding
  439. 53,
  440. waitGroup)
  441. // TODO: properly synchronize with local UDP proxy startup
  442. time.Sleep(1 * time.Second)
  443. clientUDPConn, err := net.DialUDP("udp", nil, localUDPProxyAddress)
  444. if err != nil {
  445. return fmt.Errorf("DialUDP failed: %s", err)
  446. }
  447. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  448. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  449. addrs, _, err := psiphon.ResolveIP(testHostname, clientUDPConn)
  450. clientUDPConn.Close()
  451. if err == nil && (len(addrs) == 0 || len(addrs[0]) < 4) {
  452. err = errors.New("no address")
  453. }
  454. if err != nil {
  455. return fmt.Errorf("ResolveIP failed: %s", err)
  456. }
  457. waitGroup.Wait()
  458. // Tunneled NTP request
  459. go localUDPProxy(addrs[0][len(addrs[0])-4:], 123, nil)
  460. // TODO: properly synchronize with local UDP proxy startup
  461. time.Sleep(1 * time.Second)
  462. clientUDPConn, err = net.DialUDP("udp", nil, localUDPProxyAddress)
  463. if err != nil {
  464. return fmt.Errorf("DialUDP failed: %s", err)
  465. }
  466. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  467. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  468. // NTP protocol code from: https://groups.google.com/d/msg/golang-nuts/FlcdMU5fkLQ/CAeoD9eqm-IJ
  469. ntpData := make([]byte, 48)
  470. ntpData[0] = 3<<3 | 3
  471. _, err = clientUDPConn.Write(ntpData)
  472. if err != nil {
  473. clientUDPConn.Close()
  474. return fmt.Errorf("NTP Write failed: %s", err)
  475. }
  476. _, err = clientUDPConn.Read(ntpData)
  477. if err != nil {
  478. clientUDPConn.Close()
  479. return fmt.Errorf("NTP Read failed: %s", err)
  480. }
  481. clientUDPConn.Close()
  482. var sec, frac uint64
  483. sec = uint64(ntpData[43]) | uint64(ntpData[42])<<8 | uint64(ntpData[41])<<16 | uint64(ntpData[40])<<24
  484. frac = uint64(ntpData[47]) | uint64(ntpData[46])<<8 | uint64(ntpData[45])<<16 | uint64(ntpData[44])<<24
  485. nsec := sec * 1e9
  486. nsec += (frac * 1e9) >> 32
  487. ntpNow := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)).Local()
  488. now := time.Now()
  489. diff := ntpNow.Sub(now)
  490. if diff < 0 {
  491. diff = -diff
  492. }
  493. if diff > 1*time.Minute {
  494. return fmt.Errorf("Unexpected NTP time: %s; local time: %s", ntpNow, now)
  495. }
  496. return nil
  497. }
  498. func pavePsinetDatabaseFile(t *testing.T, psinetFilename string) (string, string) {
  499. sponsorID, _ := common.MakeRandomStringHex(8)
  500. fakeDomain, _ := common.MakeRandomStringHex(4)
  501. fakePath, _ := common.MakeRandomStringHex(4)
  502. expectedHomepageURL := fmt.Sprintf("https://%s.com/%s", fakeDomain, fakePath)
  503. psinetJSONFormat := `
  504. {
  505. "sponsors": {
  506. "%s": {
  507. "home_pages": {
  508. "None": [
  509. {
  510. "region": null,
  511. "url": "%s"
  512. }
  513. ]
  514. }
  515. }
  516. }
  517. }
  518. `
  519. psinetJSON := fmt.Sprintf(psinetJSONFormat, sponsorID, expectedHomepageURL)
  520. err := ioutil.WriteFile(psinetFilename, []byte(psinetJSON), 0600)
  521. if err != nil {
  522. t.Fatalf("error paving psinet database file: %s", err)
  523. }
  524. return sponsorID, expectedHomepageURL
  525. }
  526. func paveTrafficRulesFile(t *testing.T, trafficRulesFilename, sponsorID string, deny bool) {
  527. allowTCPPorts := "443"
  528. allowUDPPorts := "53, 123"
  529. if deny {
  530. allowTCPPorts = "0"
  531. allowUDPPorts = "0"
  532. }
  533. trafficRulesJSONFormat := `
  534. {
  535. "DefaultRules" : {
  536. "RateLimits" : {
  537. "ReadBytesPerSecond": 16384,
  538. "WriteBytesPerSecond": 16384
  539. },
  540. "AllowTCPPorts" : [0],
  541. "AllowUDPPorts" : [0]
  542. },
  543. "FilteredRules" : [
  544. {
  545. "Filter" : {
  546. "HandshakeParameters" : {
  547. "sponsor_id" : ["%s"]
  548. }
  549. },
  550. "Rules" : {
  551. "RateLimits" : {
  552. "ReadUnthrottledBytes": 132352,
  553. "WriteUnthrottledBytes": 132352
  554. },
  555. "AllowTCPPorts" : [%s],
  556. "AllowUDPPorts" : [%s]
  557. }
  558. }
  559. ]
  560. }
  561. `
  562. trafficRulesJSON := fmt.Sprintf(
  563. trafficRulesJSONFormat, sponsorID, allowTCPPorts, allowUDPPorts)
  564. err := ioutil.WriteFile(trafficRulesFilename, []byte(trafficRulesJSON), 0600)
  565. if err != nil {
  566. t.Fatalf("error paving traffic rules file: %s", err)
  567. }
  568. }