1package vnet
2
3import (
4	"errors"
5	"net"
6	"sync/atomic"
7	"testing"
8	"time"
9
10	"github.com/pion/logging"
11	"github.com/stretchr/testify/assert"
12)
13
14var errNoAddress = errors.New("there must be one address")
15
16type dummyNIC struct {
17	Net
18	onInboundChunkHandler func(Chunk)
19}
20
21// hijack onInboundChunk
22func (v *dummyNIC) onInboundChunk(c Chunk) {
23	v.onInboundChunkHandler(c)
24}
25
26func getIPAddr(n NIC) (string, error) {
27	eth0, err := n.getInterface("eth0")
28	if err != nil {
29		return "", err
30	}
31
32	addrs, err := eth0.Addrs()
33	if err != nil {
34		return "", err
35	}
36
37	if len(addrs) != 1 {
38		return "", errNoAddress
39	}
40
41	return addrs[0].(*net.IPNet).IP.String(), nil
42}
43
44func TestRouterStandalone(t *testing.T) {
45	loggerFactory := logging.NewDefaultLoggerFactory()
46	log := loggerFactory.NewLogger("test")
47
48	t.Run("CIDR parsing", func(t *testing.T) {
49		r, err := NewRouter(&RouterConfig{
50			CIDR:          "1.2.3.0/24",
51			LoggerFactory: loggerFactory,
52		})
53
54		assert.Nil(t, err, "should succeed")
55		assert.Equal(t, "1.2.3.0", r.ipv4Net.IP.String(), "ip should match")
56		assert.Equal(t, "ffffff00", r.ipv4Net.Mask.String(), "mask should match")
57	})
58
59	t.Run("assignIPAddress", func(t *testing.T) {
60		r, err := NewRouter(&RouterConfig{
61			CIDR:          "1.2.3.0/24",
62			LoggerFactory: loggerFactory,
63		})
64		assert.Nil(t, err, "should succeed")
65
66		for i := 1; i < 255; i++ {
67			ip, err2 := r.assignIPAddress()
68			assert.Nil(t, err2, "should succeed")
69			assert.Equal(t, byte(1), ip[0], "should match")
70			assert.Equal(t, byte(2), ip[1], "should match")
71			assert.Equal(t, byte(3), ip[2], "should match")
72			assert.Equal(t, byte(i), ip[3], "should match")
73		}
74
75		_, err = r.assignIPAddress()
76		assert.NotNil(t, err, "should fail")
77	})
78
79	t.Run("AddNet", func(t *testing.T) {
80		r, err := NewRouter(&RouterConfig{
81			CIDR:          "1.2.3.0/24",
82			LoggerFactory: loggerFactory,
83		})
84		assert.Nil(t, err, "should succeed")
85
86		nic := NewNet(&NetConfig{})
87		assert.NotNil(t, nic, "should succeed")
88
89		err = r.AddNet(nic)
90		assert.Nil(t, err, "should succeed")
91
92		// Now, eth0 must have one address assigned
93		eth0, err := nic.v.getInterface("eth0")
94		assert.Nil(t, err, "should succeed")
95		addrs, err := eth0.Addrs()
96		assert.Nil(t, err, "should succeed")
97		assert.Equal(t, 1, len(addrs), "should match")
98		assert.Equal(t, "ip+net", addrs[0].Network(), "should match")
99		assert.Equal(t, "1.2.3.1/24", addrs[0].String(), "should match")
100		assert.Equal(t, "1.2.3.1", addrs[0].(*net.IPNet).IP.String(), "should match")
101	})
102
103	t.Run("routing", func(t *testing.T) {
104		var nCbs0 int32
105		doneCh := make(chan struct{})
106		r, err := NewRouter(&RouterConfig{
107			CIDR:          "1.2.3.0/24",
108			LoggerFactory: loggerFactory,
109		})
110		assert.Nil(t, err, "should succeed")
111
112		nic := make([]*dummyNIC, 2)
113		ip := make([]*net.UDPAddr, 2)
114
115		for i := 0; i < 2; i++ {
116			anic := NewNet(&NetConfig{})
117			assert.NotNil(t, anic, "should succeed")
118
119			nic[i] = &dummyNIC{
120				Net: *anic,
121			}
122
123			err2 := r.AddNet(nic[i])
124			assert.Nil(t, err2, "should succeed")
125
126			// Now, eth0 must have one address assigned
127			eth0, err2 := nic[i].getInterface("eth0")
128			assert.Nil(t, err2, "should succeed")
129			addrs, err2 := eth0.Addrs()
130			assert.Nil(t, err2, "should succeed")
131			assert.Equal(t, 1, len(addrs), "should match")
132			ip[i] = &net.UDPAddr{
133				IP:   addrs[0].(*net.IPNet).IP,
134				Port: 1111 * (i + 1),
135			}
136		}
137
138		nic[0].onInboundChunkHandler = func(c Chunk) {
139			log.Debugf("nic[0] received: %s", c.String())
140			atomic.AddInt32(&nCbs0, 1)
141		}
142
143		nic[1].onInboundChunkHandler = func(c Chunk) {
144			log.Debugf("nic[1] received: %s", c.String())
145			close(doneCh)
146		}
147
148		err = r.Start()
149		assert.Nil(t, err, "should succeed")
150
151		c := newChunkUDP(ip[0], ip[1])
152		r.push(c)
153
154		<-doneCh
155		err = r.Stop()
156		assert.Nil(t, err, "should succeed")
157		assert.Equal(t, int32(0), atomic.LoadInt32(&nCbs0), "should be zero")
158	})
159
160	t.Run("AddChunkFilter", func(t *testing.T) {
161		var nCbs0 int32
162		var nCbs1 int32
163		r, err := NewRouter(&RouterConfig{
164			CIDR:          "1.2.3.0/24",
165			LoggerFactory: loggerFactory,
166		})
167		assert.Nil(t, err, "should succeed")
168
169		nic := make([]*dummyNIC, 2)
170		ip := make([]*net.UDPAddr, 2)
171
172		for i := 0; i < 2; i++ {
173			anic := NewNet(&NetConfig{})
174			assert.NotNil(t, anic, "should succeed")
175
176			nic[i] = &dummyNIC{
177				Net: *anic,
178			}
179
180			err2 := r.AddNet(nic[i])
181			assert.Nil(t, err2, "should succeed")
182
183			// Now, eth0 must have one address assigned
184			eth0, err2 := nic[i].getInterface("eth0")
185			assert.Nil(t, err2, "should succeed")
186			addrs, err2 := eth0.Addrs()
187			assert.Nil(t, err2, "should succeed")
188			assert.Equal(t, 1, len(addrs), "should match")
189			ip[i] = &net.UDPAddr{
190				IP:   addrs[0].(*net.IPNet).IP,
191				Port: 1111 * (i + 1),
192			}
193		}
194
195		nic[0].onInboundChunkHandler = func(c Chunk) {
196			log.Debugf("nic[0] received: %s", c.String())
197			atomic.AddInt32(&nCbs0, 1)
198		}
199
200		var seq byte
201		nic[1].onInboundChunkHandler = func(c Chunk) {
202			log.Debugf("nic[1] received: %s", c.String())
203			seq = c.UserData()[0]
204			atomic.AddInt32(&nCbs1, 1)
205		}
206
207		// this creates a filter that block the first chunk
208		makeFilter := func(name string) func(c Chunk) bool {
209			n := 0
210			return func(c Chunk) bool {
211				pass := (n > 0)
212				if pass {
213					log.Debugf("%s passed %s", name, c.String())
214				} else {
215					log.Debugf("%s blocked %s", name, c.String())
216				}
217				n++
218				return pass
219			}
220		}
221
222		// filter 1: block first one
223		r.AddChunkFilter(makeFilter("filter1"))
224
225		// filter 2: block first one
226		r.AddChunkFilter(makeFilter("filter2"))
227
228		err = r.Start()
229		assert.Nil(t, err, "should succeed")
230
231		// send 3 packets
232		for i := 0; i < 3; i++ {
233			c := newChunkUDP(ip[0], ip[1])
234			c.userData = make([]byte, 1)
235			c.userData[0] = byte(i) // 1-byte seq num
236			r.push(c)
237		}
238
239		time.Sleep(50 * time.Millisecond)
240
241		err = r.Stop()
242		assert.Nil(t, err, "should succeed")
243		assert.Equal(t, int32(0), atomic.LoadInt32(&nCbs0), "should be zero")
244		assert.Equal(t, int32(1), atomic.LoadInt32(&nCbs1), "should be zero")
245		assert.Equal(t, byte(2), seq, "should be the last chunk")
246	})
247}
248
249func TestRouterDelay(t *testing.T) {
250	loggerFactory := logging.NewDefaultLoggerFactory()
251	log := loggerFactory.NewLogger("test")
252
253	subTest := func(t *testing.T, title string, minDelay, maxJitter time.Duration) {
254		t.Run(title, func(t *testing.T) {
255			const margin = 8 * time.Millisecond
256			var nCBs int32
257			doneCh := make(chan struct{})
258			r, err := NewRouter(&RouterConfig{
259				CIDR:          "1.2.3.0/24",
260				MinDelay:      minDelay,
261				MaxJitter:     maxJitter,
262				LoggerFactory: loggerFactory,
263			})
264			assert.Nil(t, err, "should succeed")
265
266			nic := make([]*dummyNIC, 2)
267			ip := make([]*net.UDPAddr, 2)
268
269			for i := 0; i < 2; i++ {
270				anic := NewNet(&NetConfig{})
271				assert.NotNil(t, anic, "should succeed")
272
273				nic[i] = &dummyNIC{
274					Net: *anic,
275				}
276
277				err2 := r.AddNet(nic[i])
278				assert.Nil(t, err2, "should succeed")
279
280				// Now, eth0 must have one address assigned
281				eth0, err2 := nic[i].getInterface("eth0")
282				assert.Nil(t, err2, "should succeed")
283				addrs, err2 := eth0.Addrs()
284				assert.Nil(t, err2, "should succeed")
285				assert.Equal(t, 1, len(addrs), "should match")
286				ip[i] = &net.UDPAddr{
287					IP:   addrs[0].(*net.IPNet).IP,
288					Port: 1111 * (i + 1),
289				}
290			}
291
292			var delayRes []time.Duration
293			nPkts := 1
294
295			nic[0].onInboundChunkHandler = func(c Chunk) {}
296
297			nic[1].onInboundChunkHandler = func(c Chunk) {
298				delay := time.Since(c.getTimestamp())
299				delayRes = append(delayRes, delay)
300				n := atomic.AddInt32(&nCBs, 1)
301				if n == int32(nPkts) {
302					close(doneCh)
303				}
304			}
305
306			err = r.Start()
307			assert.Nil(t, err, "should succeed")
308
309			for i := 0; i < nPkts; i++ {
310				c := newChunkUDP(ip[0], ip[1])
311				r.push(c)
312				time.Sleep(50 * time.Millisecond)
313			}
314
315			<-doneCh
316			err = r.Stop()
317			assert.Nil(t, err, "should succeed")
318
319			// Validate the amount of delays
320			for _, d := range delayRes {
321				log.Infof("min delay : %v", minDelay)
322				log.Infof("max jitter: %v", maxJitter)
323				log.Infof("actual delay: %v", d)
324				assert.True(t, d >= minDelay, "should delay >= 20ms")
325				assert.True(t, d <= (minDelay+maxJitter+margin), "should delay <= minDelay + maxJitter")
326				// Note: actual delay should be within 30ms but giving a 8ms
327				// margin for possible extra delay
328				// (e.g. wakeup delay, debug logs, etc)
329			}
330		})
331	}
332
333	subTest(t, "Delay only", 20*time.Millisecond, 0)
334	subTest(t, "Jitter only", 0, 10*time.Millisecond)
335	subTest(t, "Delay and Jitter", 20*time.Millisecond, 10*time.Millisecond)
336}
337
338func TestRouterOneChild(t *testing.T) {
339	loggerFactory := logging.NewDefaultLoggerFactory()
340	log := loggerFactory.NewLogger("test")
341
342	t.Run("lan to wan", func(t *testing.T) {
343		doneCh := make(chan struct{})
344
345		// WAN
346		wan, err := NewRouter(&RouterConfig{
347			CIDR:          "1.2.3.0/24",
348			LoggerFactory: loggerFactory,
349		})
350		assert.Nil(t, err, "should succeed")
351		assert.NotNil(t, wan, "should succeed")
352
353		wanNet := &dummyNIC{
354			Net: *NewNet(&NetConfig{}),
355		}
356
357		err = wan.AddNet(wanNet)
358		assert.Nil(t, err, "should succeed")
359
360		// Now, eth0 must have one address assigned
361		wanIP, err := getIPAddr(wanNet)
362		log.Debugf("wanIP: %s", wanIP)
363
364		// LAN
365		lan, err := NewRouter(&RouterConfig{
366			CIDR:          "192.168.0.0/24",
367			LoggerFactory: loggerFactory,
368		})
369		assert.Nil(t, err, "should succeed")
370		assert.NotNil(t, lan, "should succeed")
371
372		lanNet := &dummyNIC{
373			Net: *NewNet(&NetConfig{}),
374		}
375		err = lan.AddNet(lanNet)
376		assert.Nil(t, err, "should succeed")
377
378		// Now, eth0 must have one address assigned
379		lanIP, err := getIPAddr(lanNet)
380		log.Debugf("lanIP: %s", lanIP)
381
382		err = wan.AddRouter(lan)
383		assert.Nil(t, err, "should succeed")
384
385		lanNet.onInboundChunkHandler = func(c Chunk) {
386			log.Debugf("lanNet received: %s", c.String())
387			close(doneCh)
388		}
389
390		wanNet.onInboundChunkHandler = func(c Chunk) {
391			log.Debugf("wanNet received: %s", c.String())
392
393			// echo the chunk
394			echo := c.Clone().(*chunkUDP)
395			err = echo.setSourceAddr(c.(*chunkUDP).DestinationAddr().String())
396			assert.NoError(t, err, "should succeed")
397			err = echo.setDestinationAddr(c.(*chunkUDP).SourceAddr().String())
398			assert.NoError(t, err, "should succeed")
399
400			log.Debug("wan.push being called..")
401			wan.push(echo)
402			log.Debug("wan.push called!")
403		}
404
405		err = wan.Start()
406		assert.Nil(t, err, "should succeed")
407
408		c := newChunkUDP(
409			&net.UDPAddr{
410				IP:   net.ParseIP(lanIP),
411				Port: 1234,
412			},
413			&net.UDPAddr{
414				IP:   net.ParseIP(wanIP),
415				Port: 5678,
416			},
417		)
418
419		log.Debugf("sending %s", c.String())
420
421		lan.push(c)
422
423		<-doneCh
424		err = wan.Stop()
425		assert.Nil(t, err, "should succeed")
426	})
427}
428
429func TestRouterStaticIPs(t *testing.T) {
430	loggerFactory := logging.NewDefaultLoggerFactory()
431	// log := loggerFactory.NewLogger("test")
432
433	t.Run("more than one static IP", func(t *testing.T) {
434		lan, err := NewRouter(&RouterConfig{
435			CIDR: "192.168.0.0/24",
436			StaticIPs: []string{
437				"1.2.3.1",
438				"1.2.3.2",
439				"1.2.3.3",
440			},
441			LoggerFactory: loggerFactory,
442		})
443		assert.Nil(t, err, "should succeed")
444		assert.NotNil(t, lan, "should succeed")
445
446		assert.Equal(t, 3, len(lan.staticIPs), "should be 3")
447		assert.Equal(t, "1.2.3.1", lan.staticIPs[0].String(), "should match")
448		assert.Equal(t, "1.2.3.2", lan.staticIPs[1].String(), "should match")
449		assert.Equal(t, "1.2.3.3", lan.staticIPs[2].String(), "should match")
450	})
451
452	t.Run("StaticIPs and StaticIP in the mix", func(t *testing.T) {
453		lan, err := NewRouter(&RouterConfig{
454			CIDR: "192.168.0.0/24",
455			StaticIPs: []string{
456				"1.2.3.1",
457				"1.2.3.2",
458				"1.2.3.3",
459			},
460			StaticIP:      demoIP,
461			LoggerFactory: loggerFactory,
462		})
463		assert.Nil(t, err, "should succeed")
464		assert.NotNil(t, lan, "should succeed")
465
466		assert.Equal(t, 4, len(lan.staticIPs), "should be 4")
467		assert.Equal(t, "1.2.3.1", lan.staticIPs[0].String(), "should match")
468		assert.Equal(t, "1.2.3.2", lan.staticIPs[1].String(), "should match")
469		assert.Equal(t, "1.2.3.3", lan.staticIPs[2].String(), "should match")
470		assert.Equal(t, demoIP, lan.staticIPs[3].String(), "should match")
471	})
472
473	t.Run("Static IP and local IP mapping", func(t *testing.T) {
474		lan, err := NewRouter(&RouterConfig{
475			CIDR: "192.168.0.0/24",
476			StaticIPs: []string{
477				"1.2.3.1/192.168.0.1",
478				"1.2.3.2/192.168.0.2",
479				"1.2.3.3/192.168.0.3",
480			},
481			LoggerFactory: loggerFactory,
482		})
483		assert.NoError(t, err, "should succeed")
484		assert.NotNil(t, lan, "should succeed")
485
486		assert.Equal(t, 3, len(lan.staticIPs), "should be 3")
487		assert.Equal(t, "1.2.3.1", lan.staticIPs[0].String(), "should match")
488		assert.Equal(t, "1.2.3.2", lan.staticIPs[1].String(), "should match")
489		assert.Equal(t, "1.2.3.3", lan.staticIPs[2].String(), "should match")
490		assert.Equal(t, 3, len(lan.staticLocalIPs), "should be 3")
491		localIPs := []string{"192.168.0.1", "192.168.0.2", "192.168.0.3"}
492		for i, extIPStr := range []string{"1.2.3.1", "1.2.3.2", "1.2.3.3"} {
493			locIP, ok := lan.staticLocalIPs[extIPStr]
494			assert.True(t, ok, "should have the external IP")
495			assert.Equal(t, localIPs[i], locIP.String(), "should match")
496		}
497
498		// bad local IP
499		_, err = NewRouter(&RouterConfig{
500			CIDR: "192.168.0.0/24",
501			StaticIPs: []string{
502				"1.2.3.1/192.168.0.1",
503				"1.2.3.2/bad", // <-- invalid local IP
504			},
505			LoggerFactory: loggerFactory,
506		})
507		assert.Error(t, err, "should fail")
508
509		// local IP out of CIDR
510		_, err = NewRouter(&RouterConfig{
511			CIDR: "192.168.0.0/24",
512			StaticIPs: []string{
513				"1.2.3.1/192.168.0.1",
514				"1.2.3.2/172.16.1.2", // <-- out of CIDR
515			},
516			LoggerFactory: loggerFactory,
517		})
518		assert.Error(t, err, "should fail")
519
520		// num of local IPs mismatch
521		_, err = NewRouter(&RouterConfig{
522			CIDR: "192.168.0.0/24",
523			StaticIPs: []string{
524				"1.2.3.1/192.168.0.1",
525				"1.2.3.2", // <-- lack of local IP
526			},
527			LoggerFactory: loggerFactory,
528		})
529		assert.Error(t, err, "should fail")
530	})
531
532	t.Run("1:1 NAT configuration", func(t *testing.T) {
533		wan, err := NewRouter(&RouterConfig{
534			CIDR:          "0.0.0.0/0",
535			LoggerFactory: loggerFactory,
536		})
537		if !assert.NoError(t, err, "should succeed") {
538			return
539		}
540		if !assert.NotNil(t, wan, "should succeed") {
541			return
542		}
543
544		lan, err := NewRouter(&RouterConfig{
545			CIDR: "192.168.0.0/24",
546			StaticIPs: []string{
547				"1.2.3.1/192.168.0.1",
548				"1.2.3.2/192.168.0.2",
549				"1.2.3.3/192.168.0.3",
550			},
551			NATType: &NATType{
552				Mode: NATModeNAT1To1,
553			},
554			LoggerFactory: loggerFactory,
555		})
556		if !assert.NoError(t, err, "should succeed") {
557			return
558		}
559		if !assert.NotNil(t, lan, "should succeed") {
560			return
561		}
562
563		err = wan.AddRouter(lan)
564		if !assert.NoError(t, err, "should succeed") {
565			return
566		}
567
568		if !assert.NotNil(t, lan.nat, "should not be nil") {
569			return
570		}
571
572		assert.Equal(t, 3, len(lan.nat.mappedIPs), "should match")
573		assert.Equal(t, "1.2.3.1", lan.nat.mappedIPs[0].String(), "should match")
574		assert.Equal(t, "1.2.3.2", lan.nat.mappedIPs[1].String(), "should match")
575		assert.Equal(t, "1.2.3.3", lan.nat.mappedIPs[2].String(), "should match")
576		assert.Equal(t, 3, len(lan.nat.localIPs), "should match")
577		assert.Equal(t, "192.168.0.1", lan.nat.localIPs[0].String(), "should match")
578		assert.Equal(t, "192.168.0.2", lan.nat.localIPs[1].String(), "should match")
579		assert.Equal(t, "192.168.0.3", lan.nat.localIPs[2].String(), "should match")
580	})
581}
582
583func TestRouterFailures(t *testing.T) {
584	loggerFactory := logging.NewDefaultLoggerFactory()
585	// log := loggerFactory.NewLogger("test")
586
587	t.Run("Stop when router is stopped", func(t *testing.T) {
588		r, err := NewRouter(&RouterConfig{
589			CIDR:          "1.2.3.0/24",
590			LoggerFactory: loggerFactory,
591		})
592		assert.Nil(t, err, "should succeed")
593
594		err = r.Stop()
595		assert.Error(t, err, "should fail")
596	})
597
598	t.Run("AddNet", func(t *testing.T) {
599		r, err := NewRouter(&RouterConfig{
600			CIDR:          "1.2.3.0/24",
601			LoggerFactory: loggerFactory,
602		})
603		assert.Nil(t, err, "should succeed")
604
605		nic := NewNet(&NetConfig{
606			StaticIPs: []string{
607				"5.6.7.8", // out of parent router'c CIDR
608			},
609		})
610		assert.NotNil(t, nic, "should succeed")
611
612		err = r.AddNet(nic)
613		assert.Error(t, err, "should fail")
614	})
615
616	t.Run("AddRouter", func(t *testing.T) {
617		r1, err := NewRouter(&RouterConfig{
618			CIDR:          "1.2.3.0/24",
619			LoggerFactory: loggerFactory,
620		})
621		assert.Nil(t, err, "should succeed")
622
623		r2, err := NewRouter(&RouterConfig{
624			CIDR: "192.168.0.0/24",
625			StaticIPs: []string{
626				"5.6.7.8", // out of parent router'c CIDR
627			},
628
629			LoggerFactory: loggerFactory,
630		})
631		assert.Nil(t, err, "should succeed")
632
633		err = r1.AddRouter(r2)
634		assert.Error(t, err, "should fail")
635	})
636}
637