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