github.com/vishvananda/netlink@v1.3.0/neigh_test.go (about) 1 // +build linux 2 3 package netlink 4 5 import ( 6 "net" 7 "syscall" 8 "testing" 9 "time" 10 11 "github.com/vishvananda/netns" 12 "golang.org/x/sys/unix" 13 ) 14 15 type arpEntry struct { 16 ip net.IP 17 mac net.HardwareAddr 18 } 19 20 type proxyEntry struct { 21 ip net.IP 22 dev int 23 } 24 25 func parseMAC(s string) net.HardwareAddr { 26 m, err := net.ParseMAC(s) 27 if err != nil { 28 panic(err) 29 } 30 return m 31 } 32 33 func dumpContains(dump []Neigh, e arpEntry) bool { 34 for _, n := range dump { 35 if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 { 36 return true 37 } 38 } 39 return false 40 } 41 42 func dumpContainsNeigh(dump []Neigh, ne Neigh) bool { 43 for _, n := range dump { 44 if n.IP.Equal(ne.IP) && n.LLIPAddr.Equal(ne.LLIPAddr) { 45 return true 46 } 47 } 48 return false 49 } 50 51 func dumpContainsState(dump []Neigh, e arpEntry, s uint16) bool { 52 for _, n := range dump { 53 if n.IP.Equal(e.ip) && uint16(n.State) == s { 54 return true 55 } 56 } 57 return false 58 } 59 60 func dumpContainsProxy(dump []Neigh, p proxyEntry) bool { 61 for _, n := range dump { 62 if n.IP.Equal(p.ip) && (n.LinkIndex == p.dev) && (n.Flags&NTF_PROXY) == NTF_PROXY { 63 return true 64 } 65 } 66 return false 67 } 68 69 func TestNeighAddDelLLIPAddr(t *testing.T) { 70 setUpNetlinkTestWithKModule(t, "ip_gre") 71 72 tearDown := setUpNetlinkTest(t) 73 defer tearDown() 74 75 dummy := Gretun{ 76 LinkAttrs: LinkAttrs{Name: "neigh0"}, 77 Local: net.IPv4(127, 0, 0, 1), 78 IKey: 1234, 79 OKey: 1234} 80 if err := LinkAdd(&dummy); err != nil { 81 t.Errorf("Failed to create link: %v", err) 82 } 83 ensureIndex(dummy.Attrs()) 84 85 entry := Neigh{ 86 LinkIndex: dummy.Index, 87 State: NUD_PERMANENT, 88 IP: net.IPv4(198, 51, 100, 2), 89 LLIPAddr: net.IPv4(198, 51, 100, 1), 90 } 91 92 err := NeighAdd(&entry) 93 if err != nil { 94 t.Errorf("Failed to NeighAdd: %v", err) 95 } 96 97 // Dump and see that all added entries are there 98 dump, err := NeighList(dummy.Index, 0) 99 if err != nil { 100 t.Errorf("Failed to NeighList: %v", err) 101 } 102 103 if !dumpContainsNeigh(dump, entry) { 104 t.Errorf("Dump does not contain: %v: %v", entry, dump) 105 } 106 107 // Delete the entry 108 err = NeighDel(&entry) 109 if err != nil { 110 t.Errorf("Failed to NeighDel: %v", err) 111 } 112 113 if err := LinkDel(&dummy); err != nil { 114 t.Fatal(err) 115 } 116 } 117 118 func TestNeighAddDel(t *testing.T) { 119 tearDown := setUpNetlinkTest(t) 120 defer tearDown() 121 122 dummy := Dummy{LinkAttrs{Name: "neigh0"}} 123 if err := LinkAdd(&dummy); err != nil { 124 t.Fatal(err) 125 } 126 127 ensureIndex(dummy.Attrs()) 128 129 arpTable := []arpEntry{ 130 {net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")}, 131 {net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")}, 132 {net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")}, 133 {net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")}, 134 {net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")}, 135 } 136 137 // Add the arpTable 138 for _, entry := range arpTable { 139 err := NeighAdd(&Neigh{ 140 LinkIndex: dummy.Index, 141 State: NUD_REACHABLE, 142 IP: entry.ip, 143 HardwareAddr: entry.mac, 144 }) 145 146 if err != nil { 147 t.Errorf("Failed to NeighAdd: %v", err) 148 } 149 } 150 151 // Dump and see that all added entries are there 152 dump, err := NeighList(dummy.Index, 0) 153 if err != nil { 154 t.Errorf("Failed to NeighList: %v", err) 155 } 156 157 for _, entry := range arpTable { 158 if !dumpContains(dump, entry) { 159 t.Errorf("Dump does not contain: %v", entry) 160 } 161 } 162 163 // Delete the arpTable 164 for _, entry := range arpTable { 165 err := NeighDel(&Neigh{ 166 LinkIndex: dummy.Index, 167 IP: entry.ip, 168 HardwareAddr: entry.mac, 169 }) 170 171 if err != nil { 172 t.Errorf("Failed to NeighDel: %v", err) 173 } 174 } 175 176 // TODO: seems not working because of cache 177 //// Dump and see that none of deleted entries are there 178 //dump, err = NeighList(dummy.Index, 0) 179 //if err != nil { 180 //t.Errorf("Failed to NeighList: %v", err) 181 //} 182 183 //for _, entry := range arpTable { 184 //if dumpContains(dump, entry) { 185 //t.Errorf("Dump contains: %v", entry) 186 //} 187 //} 188 189 if err := LinkDel(&dummy); err != nil { 190 t.Fatal(err) 191 } 192 } 193 194 func TestNeighAddDelProxy(t *testing.T) { 195 tearDown := setUpNetlinkTest(t) 196 defer tearDown() 197 198 dummy := Dummy{LinkAttrs{Name: "neigh0"}} 199 if err := LinkAdd(&dummy); err != nil { 200 t.Fatal(err) 201 } 202 203 ensureIndex(dummy.Attrs()) 204 205 proxyTable := []proxyEntry{ 206 {net.ParseIP("10.99.0.1"), dummy.Index}, 207 {net.ParseIP("10.99.0.2"), dummy.Index}, 208 {net.ParseIP("10.99.0.3"), dummy.Index}, 209 {net.ParseIP("10.99.0.4"), dummy.Index}, 210 {net.ParseIP("10.99.0.5"), dummy.Index}, 211 } 212 213 // Add the proxyTable 214 for _, entry := range proxyTable { 215 err := NeighAdd(&Neigh{ 216 LinkIndex: dummy.Index, 217 Flags: NTF_PROXY, 218 IP: entry.ip, 219 }) 220 221 if err != nil { 222 t.Errorf("Failed to NeighAdd: %v", err) 223 } 224 } 225 226 // Dump and see that all added entries are there 227 dump, err := NeighProxyList(dummy.Index, 0) 228 if err != nil { 229 t.Errorf("Failed to NeighList: %v", err) 230 } 231 232 for _, entry := range proxyTable { 233 if !dumpContainsProxy(dump, entry) { 234 t.Errorf("Dump does not contain: %v", entry) 235 } 236 } 237 238 // Delete the proxyTable 239 for _, entry := range proxyTable { 240 err := NeighDel(&Neigh{ 241 LinkIndex: dummy.Index, 242 Flags: NTF_PROXY, 243 IP: entry.ip, 244 }) 245 246 if err != nil { 247 t.Errorf("Failed to NeighDel: %v", err) 248 } 249 } 250 251 // Dump and see that none of deleted entries are there 252 dump, err = NeighProxyList(dummy.Index, 0) 253 if err != nil { 254 t.Errorf("Failed to NeighList: %v", err) 255 } 256 257 for _, entry := range proxyTable { 258 if dumpContainsProxy(dump, entry) { 259 t.Errorf("Dump contains: %v", entry) 260 } 261 } 262 263 if err := LinkDel(&dummy); err != nil { 264 t.Fatal(err) 265 } 266 } 267 268 // expectNeighUpdate returns whether the expected updates are received within one second. 269 func expectNeighUpdate(ch <-chan NeighUpdate, expected []NeighUpdate) bool { 270 for { 271 timeout := time.After(time.Second) 272 select { 273 case update := <-ch: 274 var toDelete []int 275 for index, elem := range expected { 276 if update.Type == elem.Type && 277 update.Neigh.State == elem.Neigh.State && 278 update.Neigh.IP != nil && 279 update.Neigh.IP.Equal(elem.Neigh.IP) { 280 toDelete = append(toDelete, index) 281 } 282 } 283 for done, index := range toDelete { 284 expected = append(expected[:index-done], expected[index-done+1:]...) 285 } 286 if len(expected) == 0 { 287 return true 288 } 289 case <-timeout: 290 return false 291 } 292 } 293 } 294 295 func TestNeighSubscribe(t *testing.T) { 296 tearDown := setUpNetlinkTest(t) 297 defer tearDown() 298 299 dummy := &Dummy{LinkAttrs{Name: "neigh0"}} 300 if err := LinkAdd(dummy); err != nil { 301 t.Errorf("Failed to create link: %v", err) 302 } 303 ensureIndex(dummy.Attrs()) 304 defer func() { 305 if err := LinkDel(dummy); err != nil { 306 t.Fatal(err) 307 } 308 }() 309 310 ch := make(chan NeighUpdate) 311 done := make(chan struct{}) 312 defer close(done) 313 if err := NeighSubscribe(ch, done); err != nil { 314 t.Fatal(err) 315 } 316 317 entry := &Neigh{ 318 LinkIndex: dummy.Index, 319 State: NUD_REACHABLE, 320 IP: net.IPv4(10, 99, 0, 1), 321 HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"), 322 } 323 324 if err := NeighAdd(entry); err != nil { 325 t.Errorf("Failed to NeighAdd: %v", err) 326 } 327 if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{ 328 Type: unix.RTM_NEWNEIGH, 329 Neigh: *entry, 330 }}) { 331 t.Fatalf("Add update not received as expected") 332 } 333 if err := NeighDel(entry); err != nil { 334 t.Fatal(err) 335 } 336 if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{ 337 Type: unix.RTM_NEWNEIGH, 338 Neigh: Neigh{ 339 State: NUD_FAILED, 340 IP: entry.IP}, 341 }}) { 342 t.Fatalf("Del update not received as expected") 343 } 344 } 345 346 func TestNeighSubscribeWithOptions(t *testing.T) { 347 tearDown := setUpNetlinkTest(t) 348 defer tearDown() 349 350 ch := make(chan NeighUpdate) 351 done := make(chan struct{}) 352 defer close(done) 353 var lastError error 354 defer func() { 355 if lastError != nil { 356 t.Fatalf("Fatal error received during subscription: %v", lastError) 357 } 358 }() 359 if err := NeighSubscribeWithOptions(ch, done, NeighSubscribeOptions{ 360 ErrorCallback: func(err error) { 361 lastError = err 362 }, 363 }); err != nil { 364 t.Fatal(err) 365 } 366 367 dummy := &Dummy{LinkAttrs{Name: "neigh0"}} 368 if err := LinkAdd(dummy); err != nil { 369 t.Errorf("Failed to create link: %v", err) 370 } 371 ensureIndex(dummy.Attrs()) 372 defer func() { 373 if err := LinkDel(dummy); err != nil { 374 t.Fatal(err) 375 } 376 }() 377 378 entry := &Neigh{ 379 LinkIndex: dummy.Index, 380 State: NUD_REACHABLE, 381 IP: net.IPv4(10, 99, 0, 1), 382 HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"), 383 } 384 385 err := NeighAdd(entry) 386 if err != nil { 387 t.Errorf("Failed to NeighAdd: %v", err) 388 } 389 if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{ 390 Type: unix.RTM_NEWNEIGH, 391 Neigh: *entry, 392 }}) { 393 t.Fatalf("Add update not received as expected") 394 } 395 } 396 397 func TestNeighSubscribeAt(t *testing.T) { 398 skipUnlessRoot(t) 399 400 // Create an handle on a custom netns 401 newNs, err := netns.New() 402 if err != nil { 403 t.Fatal(err) 404 } 405 defer newNs.Close() 406 407 nh, err := NewHandleAt(newNs) 408 if err != nil { 409 t.Fatal(err) 410 } 411 defer nh.Close() 412 413 // Subscribe for Neigh events on the custom netns 414 ch := make(chan NeighUpdate) 415 done := make(chan struct{}) 416 defer close(done) 417 if err := NeighSubscribeAt(newNs, ch, done); err != nil { 418 t.Fatal(err) 419 } 420 421 dummy := &Dummy{LinkAttrs{Name: "neigh0"}} 422 if err := nh.LinkAdd(dummy); err != nil { 423 t.Errorf("Failed to create link: %v", err) 424 } 425 ensureIndex(dummy.Attrs()) 426 defer func() { 427 if err := nh.LinkDel(dummy); err != nil { 428 t.Fatal(err) 429 } 430 }() 431 432 entry := &Neigh{ 433 LinkIndex: dummy.Index, 434 State: NUD_REACHABLE, 435 IP: net.IPv4(198, 51, 100, 1), 436 HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"), 437 } 438 439 err = nh.NeighAdd(entry) 440 if err != nil { 441 t.Errorf("Failed to NeighAdd: %v", err) 442 } 443 if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{ 444 Type: unix.RTM_NEWNEIGH, 445 Neigh: *entry, 446 }}) { 447 t.Fatalf("Add update not received as expected") 448 } 449 } 450 451 func TestNeighSubscribeListExisting(t *testing.T) { 452 skipUnlessRoot(t) 453 454 // Create an handle on a custom netns 455 newNs, err := netns.New() 456 if err != nil { 457 t.Fatal(err) 458 } 459 defer newNs.Close() 460 461 nh, err := NewHandleAt(newNs) 462 if err != nil { 463 t.Fatal(err) 464 } 465 defer nh.Close() 466 467 dummy := &Dummy{LinkAttrs{Name: "neigh0"}} 468 if err := nh.LinkAdd(dummy); err != nil { 469 t.Errorf("Failed to create link: %v", err) 470 } 471 ensureIndex(dummy.Attrs()) 472 defer func() { 473 if err := nh.LinkDel(dummy); err != nil { 474 t.Fatal(err) 475 } 476 }() 477 478 vxlani := &Vxlan{LinkAttrs: LinkAttrs{Name: "neigh1"}, VxlanId: 1} 479 if err := nh.LinkAdd(vxlani); err != nil { 480 t.Errorf("Failed to create link: %v", err) 481 } 482 ensureIndex(vxlani.Attrs()) 483 defer func() { 484 if err := nh.LinkDel(vxlani); err != nil { 485 t.Fatal(err) 486 } 487 }() 488 489 entry1 := &Neigh{ 490 LinkIndex: dummy.Index, 491 State: NUD_REACHABLE, 492 IP: net.IPv4(198, 51, 100, 1), 493 HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"), 494 } 495 496 entryBr := &Neigh{ 497 Family: syscall.AF_BRIDGE, 498 LinkIndex: vxlani.Index, 499 State: NUD_PERMANENT, 500 Flags: NTF_SELF, 501 IP: net.IPv4(198, 51, 100, 3), 502 HardwareAddr: parseMAC("aa:bb:cc:dd:00:03"), 503 } 504 505 err = nh.NeighAdd(entry1) 506 if err != nil { 507 t.Errorf("Failed to NeighAdd: %v", err) 508 } 509 err = nh.NeighAppend(entryBr) 510 if err != nil { 511 t.Errorf("Failed to NeighAdd: %v", err) 512 } 513 514 // Subscribe for Neigh events including existing neighbors 515 ch := make(chan NeighUpdate) 516 done := make(chan struct{}) 517 defer close(done) 518 if err := NeighSubscribeWithOptions(ch, done, NeighSubscribeOptions{ 519 Namespace: &newNs, 520 ListExisting: true}, 521 ); err != nil { 522 t.Fatal(err) 523 } 524 525 if !expectNeighUpdate(ch, []NeighUpdate{ 526 NeighUpdate{ 527 Type: unix.RTM_NEWNEIGH, 528 Neigh: *entry1, 529 }, 530 NeighUpdate{ 531 Type: unix.RTM_NEWNEIGH, 532 Neigh: *entryBr, 533 }, 534 }) { 535 t.Fatalf("Existing add update not received as expected") 536 } 537 538 entry2 := &Neigh{ 539 LinkIndex: dummy.Index, 540 State: NUD_PERMANENT, 541 IP: net.IPv4(198, 51, 100, 2), 542 HardwareAddr: parseMAC("aa:bb:cc:dd:00:02"), 543 } 544 545 err = nh.NeighAdd(entry2) 546 if err != nil { 547 t.Errorf("Failed to NeighAdd: %v", err) 548 } 549 550 if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{ 551 Type: unix.RTM_NEWNEIGH, 552 Neigh: *entry2, 553 }}) { 554 t.Fatalf("Existing add update not received as expected") 555 } 556 } 557 558 func TestNeighListExecuteStateFilter(t *testing.T) { 559 tearDown := setUpNetlinkTest(t) 560 defer tearDown() 561 562 // Create dummy iface 563 dummy := Dummy{LinkAttrs{Name: "neigh0"}} 564 if err := LinkAdd(&dummy); err != nil { 565 t.Fatal(err) 566 } 567 568 ensureIndex(dummy.Attrs()) 569 570 // Define some entries 571 reachArpTable := []arpEntry{ 572 {net.ParseIP("198.51.100.1"), parseMAC("44:bb:cc:dd:00:01")}, 573 {net.ParseIP("2001:db8::1"), parseMAC("66:bb:cc:dd:00:02")}, 574 } 575 576 staleArpTable := []arpEntry{ 577 {net.ParseIP("198.51.100.10"), parseMAC("44:bb:cc:dd:00:10")}, 578 {net.ParseIP("2001:db8::10"), parseMAC("66:bb:cc:dd:00:10")}, 579 } 580 581 entries := append(reachArpTable, staleArpTable...) 582 583 // Add reachable neigh entries 584 for _, entry := range reachArpTable { 585 err := NeighAdd(&Neigh{ 586 LinkIndex: dummy.Index, 587 State: NUD_REACHABLE, 588 IP: entry.ip, 589 HardwareAddr: entry.mac, 590 }) 591 592 if err != nil { 593 t.Errorf("Failed to NeighAdd: %v", err) 594 } 595 } 596 // Add stale neigh entries 597 for _, entry := range staleArpTable { 598 err := NeighAdd(&Neigh{ 599 LinkIndex: dummy.Index, 600 State: NUD_STALE, 601 IP: entry.ip, 602 HardwareAddr: entry.mac, 603 }) 604 605 if err != nil { 606 t.Errorf("Failed to NeighAdd: %v", err) 607 } 608 } 609 610 // Dump reachable and see that all added reachable entries are present and there are no stale entries 611 dump, err := NeighListExecute(Ndmsg{ 612 Index: uint32(dummy.Index), 613 State: NUD_REACHABLE, 614 }) 615 if err != nil { 616 t.Errorf("Failed to NeighListExecute: %v", err) 617 } 618 619 for _, entry := range reachArpTable { 620 if !dumpContainsState(dump, entry, NUD_REACHABLE) { 621 t.Errorf("Dump does not contains: %v", entry) 622 } 623 } 624 for _, entry := range staleArpTable { 625 if dumpContainsState(dump, entry, NUD_STALE) { 626 t.Errorf("Dump contains: %v", entry) 627 } 628 } 629 630 // Delete all neigh entries 631 for _, entry := range entries { 632 err := NeighDel(&Neigh{ 633 LinkIndex: dummy.Index, 634 IP: entry.ip, 635 HardwareAddr: entry.mac, 636 }) 637 638 if err != nil { 639 t.Errorf("Failed to NeighDel: %v", err) 640 } 641 } 642 }