1
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
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
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
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
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
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
177
178
179
180
181
182
183
184
185
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
643
View as plain text