connectivity_vnet_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. //go:build !js
  4. // +build !js
  5. package ice
  6. import (
  7. "context"
  8. "fmt"
  9. "net"
  10. "sync/atomic"
  11. "testing"
  12. "time"
  13. "github.com/pion/logging"
  14. "github.com/pion/stun"
  15. "github.com/pion/transport/v2/test"
  16. "github.com/pion/transport/v2/vnet"
  17. "github.com/pion/turn/v2"
  18. "github.com/stretchr/testify/assert"
  19. )
  20. const (
  21. vnetGlobalIPA = "27.1.1.1"
  22. vnetLocalIPA = "192.168.0.1"
  23. vnetLocalSubnetMaskA = "24"
  24. vnetGlobalIPB = "28.1.1.1"
  25. vnetLocalIPB = "10.2.0.1"
  26. vnetLocalSubnetMaskB = "24"
  27. vnetSTUNServerIP = "1.2.3.4"
  28. vnetSTUNServerPort = 3478
  29. )
  30. type virtualNet struct {
  31. wan *vnet.Router
  32. net0 *vnet.Net
  33. net1 *vnet.Net
  34. server *turn.Server
  35. }
  36. func (v *virtualNet) close() {
  37. v.server.Close() //nolint:errcheck,gosec
  38. v.wan.Stop() //nolint:errcheck,gosec
  39. }
  40. func buildVNet(natType0, natType1 *vnet.NATType) (*virtualNet, error) {
  41. loggerFactory := logging.NewDefaultLoggerFactory()
  42. // WAN
  43. wan, err := vnet.NewRouter(&vnet.RouterConfig{
  44. CIDR: "0.0.0.0/0",
  45. LoggerFactory: loggerFactory,
  46. })
  47. if err != nil {
  48. return nil, err
  49. }
  50. wanNet, err := vnet.NewNet(&vnet.NetConfig{
  51. StaticIP: vnetSTUNServerIP, // Will be assigned to eth0
  52. })
  53. if err != nil {
  54. return nil, err
  55. }
  56. err = wan.AddNet(wanNet)
  57. if err != nil {
  58. return nil, err
  59. }
  60. // LAN 0
  61. lan0, err := vnet.NewRouter(&vnet.RouterConfig{
  62. StaticIPs: func() []string {
  63. if natType0.Mode == vnet.NATModeNAT1To1 {
  64. return []string{
  65. vnetGlobalIPA + "/" + vnetLocalIPA,
  66. }
  67. }
  68. return []string{
  69. vnetGlobalIPA,
  70. }
  71. }(),
  72. CIDR: vnetLocalIPA + "/" + vnetLocalSubnetMaskA,
  73. NATType: natType0,
  74. LoggerFactory: loggerFactory,
  75. })
  76. if err != nil {
  77. return nil, err
  78. }
  79. net0, err := vnet.NewNet(&vnet.NetConfig{
  80. StaticIPs: []string{vnetLocalIPA},
  81. })
  82. if err != nil {
  83. return nil, err
  84. }
  85. err = lan0.AddNet(net0)
  86. if err != nil {
  87. return nil, err
  88. }
  89. err = wan.AddRouter(lan0)
  90. if err != nil {
  91. return nil, err
  92. }
  93. // LAN 1
  94. lan1, err := vnet.NewRouter(&vnet.RouterConfig{
  95. StaticIPs: func() []string {
  96. if natType1.Mode == vnet.NATModeNAT1To1 {
  97. return []string{
  98. vnetGlobalIPB + "/" + vnetLocalIPB,
  99. }
  100. }
  101. return []string{
  102. vnetGlobalIPB,
  103. }
  104. }(),
  105. CIDR: vnetLocalIPB + "/" + vnetLocalSubnetMaskB,
  106. NATType: natType1,
  107. LoggerFactory: loggerFactory,
  108. })
  109. if err != nil {
  110. return nil, err
  111. }
  112. net1, err := vnet.NewNet(&vnet.NetConfig{
  113. StaticIPs: []string{vnetLocalIPB},
  114. })
  115. if err != nil {
  116. return nil, err
  117. }
  118. err = lan1.AddNet(net1)
  119. if err != nil {
  120. return nil, err
  121. }
  122. err = wan.AddRouter(lan1)
  123. if err != nil {
  124. return nil, err
  125. }
  126. // Start routers
  127. err = wan.Start()
  128. if err != nil {
  129. return nil, err
  130. }
  131. server, err := addVNetSTUN(wanNet, loggerFactory)
  132. if err != nil {
  133. return nil, err
  134. }
  135. return &virtualNet{
  136. wan: wan,
  137. net0: net0,
  138. net1: net1,
  139. server: server,
  140. }, nil
  141. }
  142. func addVNetSTUN(wanNet *vnet.Net, loggerFactory logging.LoggerFactory) (*turn.Server, error) {
  143. // Run TURN(STUN) server
  144. credMap := map[string]string{}
  145. credMap["user"] = "pass"
  146. wanNetPacketConn, err := wanNet.ListenPacket("udp", fmt.Sprintf("%s:%d", vnetSTUNServerIP, vnetSTUNServerPort))
  147. if err != nil {
  148. return nil, err
  149. }
  150. server, err := turn.NewServer(turn.ServerConfig{
  151. AuthHandler: func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) {
  152. if pw, ok := credMap[username]; ok {
  153. return turn.GenerateAuthKey(username, realm, pw), true
  154. }
  155. return nil, false
  156. },
  157. PacketConnConfigs: []turn.PacketConnConfig{
  158. {
  159. PacketConn: wanNetPacketConn,
  160. RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{
  161. RelayAddress: net.ParseIP(vnetSTUNServerIP),
  162. Address: "0.0.0.0",
  163. Net: wanNet,
  164. },
  165. },
  166. },
  167. Realm: "pion.ly",
  168. LoggerFactory: loggerFactory,
  169. })
  170. if err != nil {
  171. return nil, err
  172. }
  173. return server, err
  174. }
  175. func connectWithVNet(aAgent, bAgent *Agent) (*Conn, *Conn) {
  176. // Manual signaling
  177. aUfrag, aPwd, err := aAgent.GetLocalUserCredentials()
  178. check(err)
  179. bUfrag, bPwd, err := bAgent.GetLocalUserCredentials()
  180. check(err)
  181. gatherAndExchangeCandidates(aAgent, bAgent)
  182. accepted := make(chan struct{})
  183. var aConn *Conn
  184. go func() {
  185. var acceptErr error
  186. aConn, acceptErr = aAgent.Accept(context.TODO(), bUfrag, bPwd)
  187. check(acceptErr)
  188. close(accepted)
  189. }()
  190. bConn, err := bAgent.Dial(context.TODO(), aUfrag, aPwd)
  191. check(err)
  192. // Ensure accepted
  193. <-accepted
  194. return aConn, bConn
  195. }
  196. type agentTestConfig struct {
  197. urls []*stun.URI
  198. nat1To1IPCandidateType CandidateType
  199. }
  200. func pipeWithVNet(v *virtualNet, a0TestConfig, a1TestConfig *agentTestConfig) (*Conn, *Conn) {
  201. aNotifier, aConnected := onConnected()
  202. bNotifier, bConnected := onConnected()
  203. var nat1To1IPs []string
  204. if a0TestConfig.nat1To1IPCandidateType != CandidateTypeUnspecified {
  205. nat1To1IPs = []string{
  206. vnetGlobalIPA,
  207. }
  208. }
  209. cfg0 := &AgentConfig{
  210. Urls: a0TestConfig.urls,
  211. NetworkTypes: supportedNetworkTypes(),
  212. MulticastDNSMode: MulticastDNSModeDisabled,
  213. NAT1To1IPs: nat1To1IPs,
  214. NAT1To1IPCandidateType: a0TestConfig.nat1To1IPCandidateType,
  215. Net: v.net0,
  216. }
  217. aAgent, err := NewAgent(cfg0)
  218. if err != nil {
  219. panic(err)
  220. }
  221. err = aAgent.OnConnectionStateChange(aNotifier)
  222. if err != nil {
  223. panic(err)
  224. }
  225. if a1TestConfig.nat1To1IPCandidateType != CandidateTypeUnspecified {
  226. nat1To1IPs = []string{
  227. vnetGlobalIPB,
  228. }
  229. }
  230. cfg1 := &AgentConfig{
  231. Urls: a1TestConfig.urls,
  232. NetworkTypes: supportedNetworkTypes(),
  233. MulticastDNSMode: MulticastDNSModeDisabled,
  234. NAT1To1IPs: nat1To1IPs,
  235. NAT1To1IPCandidateType: a1TestConfig.nat1To1IPCandidateType,
  236. Net: v.net1,
  237. }
  238. bAgent, err := NewAgent(cfg1)
  239. if err != nil {
  240. panic(err)
  241. }
  242. err = bAgent.OnConnectionStateChange(bNotifier)
  243. if err != nil {
  244. panic(err)
  245. }
  246. aConn, bConn := connectWithVNet(aAgent, bAgent)
  247. // Ensure pair selected
  248. // Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
  249. <-aConnected
  250. <-bConnected
  251. return aConn, bConn
  252. }
  253. func closePipe(t *testing.T, ca *Conn, cb *Conn) bool {
  254. err := ca.Close()
  255. if !assert.NoError(t, err, "should succeed") {
  256. return false
  257. }
  258. err = cb.Close()
  259. return assert.NoError(t, err, "should succeed")
  260. }
  261. func TestConnectivityVNet(t *testing.T) {
  262. report := test.CheckRoutines(t)
  263. defer report()
  264. stunServerURL := &stun.URI{
  265. Scheme: stun.SchemeTypeSTUN,
  266. Host: vnetSTUNServerIP,
  267. Port: vnetSTUNServerPort,
  268. Proto: stun.ProtoTypeUDP,
  269. }
  270. turnServerURL := &stun.URI{
  271. Scheme: stun.SchemeTypeTURN,
  272. Host: vnetSTUNServerIP,
  273. Port: vnetSTUNServerPort,
  274. Username: "user",
  275. Password: "pass",
  276. Proto: stun.ProtoTypeUDP,
  277. }
  278. t.Run("Full-cone NATs on both ends", func(t *testing.T) {
  279. loggerFactory := logging.NewDefaultLoggerFactory()
  280. log := loggerFactory.NewLogger("test")
  281. // buildVNet with a Full-cone NATs both LANs
  282. natType := &vnet.NATType{
  283. MappingBehavior: vnet.EndpointIndependent,
  284. FilteringBehavior: vnet.EndpointIndependent,
  285. }
  286. v, err := buildVNet(natType, natType)
  287. if !assert.NoError(t, err, "should succeed") {
  288. return
  289. }
  290. defer v.close()
  291. log.Debug("Connecting...")
  292. a0TestConfig := &agentTestConfig{
  293. urls: []*stun.URI{
  294. stunServerURL,
  295. },
  296. }
  297. a1TestConfig := &agentTestConfig{
  298. urls: []*stun.URI{
  299. stunServerURL,
  300. },
  301. }
  302. ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig)
  303. time.Sleep(1 * time.Second)
  304. log.Debug("Closing...")
  305. if !closePipe(t, ca, cb) {
  306. return
  307. }
  308. })
  309. t.Run("Symmetric NATs on both ends", func(t *testing.T) {
  310. loggerFactory := logging.NewDefaultLoggerFactory()
  311. log := loggerFactory.NewLogger("test")
  312. // buildVNet with a Symmetric NATs for both LANs
  313. natType := &vnet.NATType{
  314. MappingBehavior: vnet.EndpointAddrPortDependent,
  315. FilteringBehavior: vnet.EndpointAddrPortDependent,
  316. }
  317. v, err := buildVNet(natType, natType)
  318. if !assert.NoError(t, err, "should succeed") {
  319. return
  320. }
  321. defer v.close()
  322. log.Debug("Connecting...")
  323. a0TestConfig := &agentTestConfig{
  324. urls: []*stun.URI{
  325. stunServerURL,
  326. turnServerURL,
  327. },
  328. }
  329. a1TestConfig := &agentTestConfig{
  330. urls: []*stun.URI{
  331. stunServerURL,
  332. },
  333. }
  334. ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig)
  335. log.Debug("Closing...")
  336. if !closePipe(t, ca, cb) {
  337. return
  338. }
  339. })
  340. t.Run("1:1 NAT with host candidate vs Symmetric NATs", func(t *testing.T) {
  341. loggerFactory := logging.NewDefaultLoggerFactory()
  342. log := loggerFactory.NewLogger("test")
  343. // Agent0 is behind 1:1 NAT
  344. natType0 := &vnet.NATType{
  345. Mode: vnet.NATModeNAT1To1,
  346. }
  347. // Agent1 is behind a symmetric NAT
  348. natType1 := &vnet.NATType{
  349. MappingBehavior: vnet.EndpointAddrPortDependent,
  350. FilteringBehavior: vnet.EndpointAddrPortDependent,
  351. }
  352. v, err := buildVNet(natType0, natType1)
  353. if !assert.NoError(t, err, "should succeed") {
  354. return
  355. }
  356. defer v.close()
  357. log.Debug("Connecting...")
  358. a0TestConfig := &agentTestConfig{
  359. urls: []*stun.URI{},
  360. nat1To1IPCandidateType: CandidateTypeHost, // Use 1:1 NAT IP as a host candidate
  361. }
  362. a1TestConfig := &agentTestConfig{
  363. urls: []*stun.URI{},
  364. }
  365. ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig)
  366. log.Debug("Closing...")
  367. if !closePipe(t, ca, cb) {
  368. return
  369. }
  370. })
  371. t.Run("1:1 NAT with srflx candidate vs Symmetric NATs", func(t *testing.T) {
  372. loggerFactory := logging.NewDefaultLoggerFactory()
  373. log := loggerFactory.NewLogger("test")
  374. // Agent0 is behind 1:1 NAT
  375. natType0 := &vnet.NATType{
  376. Mode: vnet.NATModeNAT1To1,
  377. }
  378. // Agent1 is behind a symmetric NAT
  379. natType1 := &vnet.NATType{
  380. MappingBehavior: vnet.EndpointAddrPortDependent,
  381. FilteringBehavior: vnet.EndpointAddrPortDependent,
  382. }
  383. v, err := buildVNet(natType0, natType1)
  384. if !assert.NoError(t, err, "should succeed") {
  385. return
  386. }
  387. defer v.close()
  388. log.Debug("Connecting...")
  389. a0TestConfig := &agentTestConfig{
  390. urls: []*stun.URI{},
  391. nat1To1IPCandidateType: CandidateTypeServerReflexive, // Use 1:1 NAT IP as a srflx candidate
  392. }
  393. a1TestConfig := &agentTestConfig{
  394. urls: []*stun.URI{},
  395. }
  396. ca, cb := pipeWithVNet(v, a0TestConfig, a1TestConfig)
  397. log.Debug("Closing...")
  398. if !closePipe(t, ca, cb) {
  399. return
  400. }
  401. })
  402. }
  403. // TestDisconnectedToConnected asserts that an agent can go to disconnected, and then return to connected successfully
  404. func TestDisconnectedToConnected(t *testing.T) {
  405. report := test.CheckRoutines(t)
  406. defer report()
  407. lim := test.TimeOut(time.Second * 10)
  408. defer lim.Stop()
  409. loggerFactory := logging.NewDefaultLoggerFactory()
  410. // Create a network with two interfaces
  411. wan, err := vnet.NewRouter(&vnet.RouterConfig{
  412. CIDR: "0.0.0.0/0",
  413. LoggerFactory: loggerFactory,
  414. })
  415. assert.NoError(t, err)
  416. var dropAllData uint64
  417. wan.AddChunkFilter(func(vnet.Chunk) bool {
  418. return atomic.LoadUint64(&dropAllData) != 1
  419. })
  420. net0, err := vnet.NewNet(&vnet.NetConfig{
  421. StaticIPs: []string{"192.168.0.1"},
  422. })
  423. assert.NoError(t, err)
  424. assert.NoError(t, wan.AddNet(net0))
  425. net1, err := vnet.NewNet(&vnet.NetConfig{
  426. StaticIPs: []string{"192.168.0.2"},
  427. })
  428. assert.NoError(t, err)
  429. assert.NoError(t, wan.AddNet(net1))
  430. assert.NoError(t, wan.Start())
  431. disconnectTimeout := time.Second
  432. keepaliveInterval := time.Millisecond * 20
  433. // Create two agents and connect them
  434. controllingAgent, err := NewAgent(&AgentConfig{
  435. NetworkTypes: supportedNetworkTypes(),
  436. MulticastDNSMode: MulticastDNSModeDisabled,
  437. Net: net0,
  438. DisconnectedTimeout: &disconnectTimeout,
  439. KeepaliveInterval: &keepaliveInterval,
  440. CheckInterval: &keepaliveInterval,
  441. })
  442. assert.NoError(t, err)
  443. controlledAgent, err := NewAgent(&AgentConfig{
  444. NetworkTypes: supportedNetworkTypes(),
  445. MulticastDNSMode: MulticastDNSModeDisabled,
  446. Net: net1,
  447. DisconnectedTimeout: &disconnectTimeout,
  448. KeepaliveInterval: &keepaliveInterval,
  449. CheckInterval: &keepaliveInterval,
  450. })
  451. assert.NoError(t, err)
  452. controllingStateChanges := make(chan ConnectionState, 100)
  453. assert.NoError(t, controllingAgent.OnConnectionStateChange(func(c ConnectionState) {
  454. controllingStateChanges <- c
  455. }))
  456. controlledStateChanges := make(chan ConnectionState, 100)
  457. assert.NoError(t, controlledAgent.OnConnectionStateChange(func(c ConnectionState) {
  458. controlledStateChanges <- c
  459. }))
  460. connectWithVNet(controllingAgent, controlledAgent)
  461. blockUntilStateSeen := func(expectedState ConnectionState, stateQueue chan ConnectionState) {
  462. for s := range stateQueue {
  463. if s == expectedState {
  464. return
  465. }
  466. }
  467. }
  468. // Assert we have gone to connected
  469. blockUntilStateSeen(ConnectionStateConnected, controllingStateChanges)
  470. blockUntilStateSeen(ConnectionStateConnected, controlledStateChanges)
  471. // Drop all packets, and block until we have gone to disconnected
  472. atomic.StoreUint64(&dropAllData, 1)
  473. blockUntilStateSeen(ConnectionStateDisconnected, controllingStateChanges)
  474. blockUntilStateSeen(ConnectionStateDisconnected, controlledStateChanges)
  475. // Allow all packets through again, block until we have gone to connected
  476. atomic.StoreUint64(&dropAllData, 0)
  477. blockUntilStateSeen(ConnectionStateConnected, controllingStateChanges)
  478. blockUntilStateSeen(ConnectionStateConnected, controlledStateChanges)
  479. assert.NoError(t, wan.Stop())
  480. assert.NoError(t, controllingAgent.Close())
  481. assert.NoError(t, controlledAgent.Close())
  482. }
  483. // Agent.Write should use the best valid pair if a selected pair is not yet available
  484. func TestWriteUseValidPair(t *testing.T) {
  485. report := test.CheckRoutines(t)
  486. defer report()
  487. lim := test.TimeOut(time.Second * 10)
  488. defer lim.Stop()
  489. loggerFactory := logging.NewDefaultLoggerFactory()
  490. // Create a network with two interfaces
  491. wan, err := vnet.NewRouter(&vnet.RouterConfig{
  492. CIDR: "0.0.0.0/0",
  493. LoggerFactory: loggerFactory,
  494. })
  495. assert.NoError(t, err)
  496. wan.AddChunkFilter(func(c vnet.Chunk) bool {
  497. if stun.IsMessage(c.UserData()) {
  498. m := &stun.Message{
  499. Raw: c.UserData(),
  500. }
  501. if decErr := m.Decode(); decErr != nil {
  502. return false
  503. } else if m.Contains(stun.AttrUseCandidate) {
  504. return false
  505. }
  506. }
  507. return true
  508. })
  509. net0, err := vnet.NewNet(&vnet.NetConfig{
  510. StaticIPs: []string{"192.168.0.1"},
  511. })
  512. assert.NoError(t, err)
  513. assert.NoError(t, wan.AddNet(net0))
  514. net1, err := vnet.NewNet(&vnet.NetConfig{
  515. StaticIPs: []string{"192.168.0.2"},
  516. })
  517. assert.NoError(t, err)
  518. assert.NoError(t, wan.AddNet(net1))
  519. assert.NoError(t, wan.Start())
  520. // Create two agents and connect them
  521. controllingAgent, err := NewAgent(&AgentConfig{
  522. NetworkTypes: supportedNetworkTypes(),
  523. MulticastDNSMode: MulticastDNSModeDisabled,
  524. Net: net0,
  525. })
  526. assert.NoError(t, err)
  527. controlledAgent, err := NewAgent(&AgentConfig{
  528. NetworkTypes: supportedNetworkTypes(),
  529. MulticastDNSMode: MulticastDNSModeDisabled,
  530. Net: net1,
  531. })
  532. assert.NoError(t, err)
  533. gatherAndExchangeCandidates(controllingAgent, controlledAgent)
  534. controllingUfrag, controllingPwd, err := controllingAgent.GetLocalUserCredentials()
  535. assert.NoError(t, err)
  536. controlledUfrag, controlledPwd, err := controlledAgent.GetLocalUserCredentials()
  537. assert.NoError(t, err)
  538. assert.NoError(t, controllingAgent.startConnectivityChecks(true, controlledUfrag, controlledPwd))
  539. assert.NoError(t, controlledAgent.startConnectivityChecks(false, controllingUfrag, controllingPwd))
  540. testMessage := []byte("Test Message")
  541. go func() {
  542. for {
  543. if _, writeErr := (&Conn{agent: controllingAgent}).Write(testMessage); writeErr != nil {
  544. return
  545. }
  546. time.Sleep(20 * time.Millisecond)
  547. }
  548. }()
  549. readBuf := make([]byte, len(testMessage))
  550. _, err = (&Conn{agent: controlledAgent}).Read(readBuf)
  551. assert.NoError(t, err)
  552. assert.Equal(t, readBuf, testMessage)
  553. assert.NoError(t, wan.Stop())
  554. assert.NoError(t, controllingAgent.Close())
  555. assert.NoError(t, controlledAgent.Close())
  556. }