1
2
3
4 package netlink
5
6 import (
7 "encoding/binary"
8 "fmt"
9 "net"
10 "os"
11 "runtime"
12 "testing"
13 "time"
14
15 "github.com/vishvananda/netlink/nl"
16 "github.com/vishvananda/netns"
17 "golang.org/x/sys/unix"
18 )
19
20 func CheckErrorFail(t *testing.T, err error) {
21 if err != nil {
22 t.Fatalf("Fatal Error: %s", err)
23 }
24 }
25 func CheckError(t *testing.T, err error) {
26 if err != nil {
27 t.Errorf("Error: %s", err)
28 }
29 }
30
31 func udpFlowCreateProg(t *testing.T, flows, srcPort int, dstIP string, dstPort int) {
32 for i := 0; i < flows; i++ {
33 ServerAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dstIP, dstPort))
34 CheckError(t, err)
35
36 LocalAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", srcPort+i))
37 CheckError(t, err)
38
39 Conn, err := net.DialUDP("udp", LocalAddr, ServerAddr)
40 CheckError(t, err)
41
42 Conn.Write([]byte("Hello World"))
43 Conn.Close()
44 }
45 }
46
47 func nsCreateAndEnter(t *testing.T) (*netns.NsHandle, *netns.NsHandle, *Handle) {
48
49 runtime.LockOSThread()
50
51
52 origns, _ := netns.Get()
53
54 ns, err := netns.New()
55 CheckErrorFail(t, err)
56
57 h, err := NewHandleAt(ns)
58 CheckErrorFail(t, err)
59
60
61 netns.Set(ns)
62
63
64 link, _ := h.LinkByName("lo")
65 h.LinkSetUp(link)
66
67 return &origns, &ns, h
68 }
69
70 func applyFilter(flowList []ConntrackFlow, ipv4Filter *ConntrackFilter, ipv6Filter *ConntrackFilter) (ipv4Match, ipv6Match uint) {
71 for _, flow := range flowList {
72 if ipv4Filter.MatchConntrackFlow(&flow) == true {
73 ipv4Match++
74 }
75 if ipv6Filter.MatchConntrackFlow(&flow) == true {
76 ipv6Match++
77 }
78 }
79 return ipv4Match, ipv6Match
80 }
81
82
83 func TestConntrackSocket(t *testing.T) {
84 skipUnlessRoot(t)
85 setUpNetlinkTestWithKModule(t, "nf_conntrack")
86 setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
87
88 h, err := NewHandle(unix.NETLINK_NETFILTER)
89 CheckErrorFail(t, err)
90
91 if h.SupportsNetlinkFamily(unix.NETLINK_NETFILTER) != true {
92 t.Fatal("ERROR not supporting the NETFILTER family")
93 }
94 }
95
96
97
98 func TestConntrackTableList(t *testing.T) {
99 if os.Getenv("CI") == "true" {
100 t.Skipf("Fails in CI: Flow creation fails")
101 }
102 skipUnlessRoot(t)
103 k, m, err := KernelVersion()
104 if err != nil {
105 t.Fatal(err)
106 }
107
108
109 if k < 4 || k == 4 && m < 19 {
110 setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
111 setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv6")
112 }
113 setUpNetlinkTestWithKModule(t, "nf_conntrack")
114 setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
115
116
117 origns, ns, h := nsCreateAndEnter(t)
118 defer netns.Set(*origns)
119 defer origns.Close()
120 defer ns.Close()
121 defer runtime.UnlockOSThread()
122
123 setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_acct", "1")
124 setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_timestamp", "1")
125 setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_udp_timeout", "45")
126
127
128 err = h.ConntrackTableFlush(ConntrackTable)
129 CheckErrorFail(t, err)
130
131
132 startTime := time.Now()
133 udpFlowCreateProg(t, 5, 2000, "127.0.0.10", 3000)
134
135
136 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
137 CheckErrorFail(t, err)
138
139
140 var found int
141 for _, flow := range flows {
142 if flow.Forward.Protocol == 17 &&
143 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
144 flow.Forward.DstPort == 3000 &&
145 (flow.Forward.SrcPort >= 2000 && flow.Forward.SrcPort <= 2005) {
146 found++
147 flowStart := time.Unix(0, int64(flow.TimeStart))
148 if flowStart.Before(startTime) || flowStart.Sub(startTime) > time.Second {
149 t.Error("Invalid conntrack entry start timestamp")
150 }
151 if flow.TimeStop != 0 {
152 t.Error("Invalid conntrack entry stop timestamp")
153 }
154
155 if flow.TimeOut < 44 || flow.TimeOut > 45 {
156 t.Error("Invalid conntrack entry timeout")
157 }
158 }
159
160 if flow.Forward.Bytes == 0 && flow.Forward.Packets == 0 && flow.Reverse.Bytes == 0 && flow.Reverse.Packets == 0 {
161 t.Error("No traffic statistics are collected")
162 }
163 }
164 if found != 5 {
165 t.Fatalf("Found only %d flows over 5", found)
166 }
167
168
169 _, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET6)
170 CheckErrorFail(t, err)
171
172
173 netns.Set(*origns)
174 }
175
176
177
178 func TestConntrackTableFlush(t *testing.T) {
179 if os.Getenv("CI") == "true" {
180 t.Skipf("Fails in CI: Flow creation fails")
181 }
182 skipUnlessRoot(t)
183 setUpNetlinkTestWithKModule(t, "nf_conntrack")
184 setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
185 k, m, err := KernelVersion()
186 if err != nil {
187 t.Fatal(err)
188 }
189
190
191 if k < 4 || k == 4 && m < 19 {
192 setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
193 }
194 setUpNetlinkTestWithKModule(t, "nf_conntrack")
195
196 origns, ns, h := nsCreateAndEnter(t)
197 defer netns.Set(*origns)
198 defer origns.Close()
199 defer ns.Close()
200 defer runtime.UnlockOSThread()
201
202
203 udpFlowCreateProg(t, 5, 3000, "127.0.0.10", 4000)
204
205
206 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
207 CheckErrorFail(t, err)
208
209
210 var found int
211 for _, flow := range flows {
212 if flow.Forward.Protocol == 17 &&
213 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
214 flow.Forward.DstPort == 4000 &&
215 (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
216 found++
217 }
218 }
219 if found != 5 {
220 t.Fatalf("Found only %d flows over 5", found)
221 }
222
223
224 err = h.ConntrackTableFlush(ConntrackTable)
225 CheckErrorFail(t, err)
226
227
228 flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET)
229 CheckErrorFail(t, err)
230
231
232 found = 0
233 for _, flow := range flows {
234 if flow.Forward.Protocol == 17 &&
235 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
236 flow.Forward.DstPort == 4000 &&
237 (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
238 found++
239 }
240 }
241 if found > 0 {
242 t.Fatalf("Found %d flows, they should had been flushed", found)
243 }
244
245
246 netns.Set(*origns)
247 }
248
249
250
251 func TestConntrackTableDelete(t *testing.T) {
252 if os.Getenv("CI") == "true" {
253 t.Skipf("Fails in CI: Flow creation fails")
254 }
255 skipUnlessRoot(t)
256
257 requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
258 k, m, err := KernelVersion()
259 if err != nil {
260 t.Fatal(err)
261 }
262
263
264 if k < 4 || k == 4 && m < 19 {
265 requiredModules = append(requiredModules, "nf_conntrack_ipv4")
266 }
267
268 setUpNetlinkTestWithKModule(t, requiredModules...)
269
270
271 origns, ns, h := nsCreateAndEnter(t)
272 defer netns.Set(*origns)
273 defer origns.Close()
274 defer ns.Close()
275 defer runtime.UnlockOSThread()
276
277
278 udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000)
279 udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000)
280
281
282 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
283 CheckErrorFail(t, err)
284
285
286 var groupA int
287 var groupB int
288 for _, flow := range flows {
289 if flow.Forward.Protocol == 17 &&
290 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
291 flow.Forward.DstPort == 6000 &&
292 (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
293 groupA++
294 }
295 if flow.Forward.Protocol == 17 &&
296 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
297 flow.Forward.DstPort == 8000 &&
298 (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
299 groupB++
300 }
301 }
302 if groupA != 5 || groupB != 5 {
303 t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB)
304 }
305
306
307 filter := &ConntrackFilter{}
308 filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
309 filter.AddProtocol(17)
310 filter.AddPort(ConntrackOrigDstPort, 8000)
311
312
313 var deleted uint
314 if deleted, err = h.ConntrackDeleteFilters(ConntrackTable, unix.AF_INET, filter); err != nil {
315 t.Fatalf("Error during the erase: %s", err)
316 }
317 if deleted != 5 {
318 t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted)
319 }
320
321
322 flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET)
323 CheckErrorFail(t, err)
324
325
326 groupA = 0
327 groupB = 0
328 for _, flow := range flows {
329 if flow.Forward.Protocol == 17 &&
330 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
331 flow.Forward.DstPort == 6000 &&
332 (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
333 groupA++
334 }
335 if flow.Forward.Protocol == 17 &&
336 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
337 flow.Forward.DstPort == 8000 &&
338 (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
339 groupB++
340 }
341 }
342 if groupA != 5 || groupB > 0 {
343 t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB)
344 }
345
346
347 netns.Set(*origns)
348 }
349
350 func TestConntrackFilter(t *testing.T) {
351 var flowList []ConntrackFlow
352 flowList = append(flowList, ConntrackFlow{
353 FamilyType: unix.AF_INET,
354 Forward: IPTuple{
355 SrcIP: net.ParseIP("10.0.0.1"),
356 DstIP: net.ParseIP("20.0.0.1"),
357 SrcPort: 1000,
358 DstPort: 2000,
359 Protocol: 17,
360 },
361 Reverse: IPTuple{
362 SrcIP: net.ParseIP("20.0.0.1"),
363 DstIP: net.ParseIP("192.168.1.1"),
364 SrcPort: 2000,
365 DstPort: 1000,
366 Protocol: 17,
367 },
368 },
369 ConntrackFlow{
370 FamilyType: unix.AF_INET,
371 Forward: IPTuple{
372 SrcIP: net.ParseIP("10.0.0.2"),
373 DstIP: net.ParseIP("20.0.0.2"),
374 SrcPort: 5000,
375 DstPort: 6000,
376 Protocol: 6,
377 },
378 Reverse: IPTuple{
379 SrcIP: net.ParseIP("20.0.0.2"),
380 DstIP: net.ParseIP("192.168.1.1"),
381 SrcPort: 6000,
382 DstPort: 5000,
383 Protocol: 6,
384 },
385 Labels: []byte{0, 0, 0, 0, 3, 4, 61, 141, 207, 170, 2, 0, 0, 0, 0, 0},
386 Zone: 200,
387 },
388 ConntrackFlow{
389 FamilyType: unix.AF_INET6,
390 Forward: IPTuple{
391 SrcIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
392 DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
393 SrcPort: 1000,
394 DstPort: 2000,
395 Protocol: 132,
396 },
397 Reverse: IPTuple{
398 SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
399 DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
400 SrcPort: 2000,
401 DstPort: 1000,
402 Protocol: 132,
403 },
404 Zone: 200,
405 })
406
407
408 v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{})
409 if v4Match > 0 || v6Match > 0 {
410 t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
411 }
412
413
414
415
416 filter := &ConntrackFilter{}
417 err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
418 if err != nil {
419 t.Fatalf("Unexpected error: %v", err)
420 }
421 if err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")); err == nil {
422 t.Fatalf("Error, it should fail adding same attribute to the filter")
423 }
424 err = filter.AddProtocol(6)
425 if err != nil {
426 t.Fatalf("Unexpected error: %v", err)
427 }
428 if err := filter.AddProtocol(17); err == nil {
429 t.Fatalf("Error, it should fail adding same attribute to the filter")
430 }
431 filter.AddPort(ConntrackOrigSrcPort, 80)
432 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
433 t.Fatalf("Error, it should fail adding same attribute to the filter")
434 }
435
436
437 filter = &ConntrackFilter{}
438 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
439 t.Fatalf("Error, it should fail adding a port filter without a protocol")
440 }
441
442
443 filter = &ConntrackFilter{}
444 err = filter.AddProtocol(47)
445 if err != nil {
446 t.Fatalf("Unexpected error: %v", err)
447 }
448 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
449 t.Fatalf("Error, it should fail adding a port filter with a wrong protocol")
450 }
451
452
453 filterV4 := &ConntrackFilter{}
454 err = filterV4.AddProtocol(6)
455 if err != nil {
456 t.Fatalf("Unexpected error: %v", err)
457 }
458
459 filterV6 := &ConntrackFilter{}
460 err = filterV6.AddProtocol(132)
461 if err != nil {
462 t.Fatalf("Unexpected error: %v", err)
463 }
464
465 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
466 if v4Match != 1 || v6Match != 1 {
467 t.Fatalf("Error, there should be only 1 match for TCP:%d, UDP:%d", v4Match, v6Match)
468 }
469
470
471 filterV4 = &ConntrackFilter{}
472 err = filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
473 if err != nil {
474 t.Fatalf("Unexpected error: %v", err)
475 }
476
477 filterV6 = &ConntrackFilter{}
478 err = filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
479 if err != nil {
480 t.Fatalf("Unexpected error: %v", err)
481 }
482
483 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
484 if v4Match != 1 || v6Match != 1 {
485 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
486 }
487
488
489 filterV4 = &ConntrackFilter{}
490 err = filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1"))
491 if err != nil {
492 t.Fatalf("Unexpected error: %v", err)
493 }
494
495 filterV6 = &ConntrackFilter{}
496 err = filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
497 if err != nil {
498 t.Fatalf("Unexpected error: %v", err)
499 }
500
501 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
502 if v4Match != 1 || v6Match != 1 {
503 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
504 }
505
506
507 filterV4 = &ConntrackFilter{}
508 err = filterV4.AddIP(ConntrackReplySrcIP, net.ParseIP("20.0.0.1"))
509 if err != nil {
510 t.Fatalf("Unexpected error: %v", err)
511 }
512
513 filterV6 = &ConntrackFilter{}
514 err = filterV6.AddIP(ConntrackReplySrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
515 if err != nil {
516 t.Fatalf("Unexpected error: %v", err)
517 }
518
519 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
520 if v4Match != 1 || v6Match != 1 {
521 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
522 }
523
524
525 filterV4 = &ConntrackFilter{}
526 err = filterV4.AddIP(ConntrackReplyDstIP, net.ParseIP("192.168.1.1"))
527 if err != nil {
528 t.Fatalf("Unexpected error: %v", err)
529 }
530
531 filterV6 = &ConntrackFilter{}
532 err = filterV6.AddIP(ConntrackReplyDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
533 if err != nil {
534 t.Fatalf("Unexpected error: %v", err)
535 }
536
537 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
538 if v4Match != 2 || v6Match != 0 {
539 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
540 }
541
542
543 filterV4 = &ConntrackFilter{}
544 err = filterV4.AddIP(ConntrackReplyAnyIP, net.ParseIP("192.168.1.1"))
545 if err != nil {
546 t.Fatalf("Unexpected error: %v", err)
547 }
548
549 filterV6 = &ConntrackFilter{}
550 err = filterV6.AddIP(ConntrackReplyAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
551 if err != nil {
552 t.Fatalf("Unexpected error: %v", err)
553 }
554
555 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
556 if v4Match != 2 || v6Match != 1 {
557 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
558 }
559
560
561 filterV4 = &ConntrackFilter{}
562 ipNet, err := ParseIPNet("10.0.0.0/12")
563 if err != nil {
564 t.Fatalf("Unexpected error: %v", err)
565 }
566 err = filterV4.AddIPNet(ConntrackOrigSrcIP, ipNet)
567 if err != nil {
568 t.Fatalf("Unexpected error: %v", err)
569 }
570
571 filterV6 = &ConntrackFilter{}
572 ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
573 if err != nil {
574 t.Fatalf("Unexpected error: %v", err)
575 }
576 err = filterV6.AddIPNet(ConntrackOrigSrcIP, ipNet)
577 if err != nil {
578 t.Fatalf("Unexpected error: %v", err)
579 }
580
581 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
582 if v4Match != 2 || v6Match != 1 {
583 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
584 }
585
586
587 filterV4 = &ConntrackFilter{}
588 ipNet, err = ParseIPNet("20.0.0.0/12")
589 if err != nil {
590 t.Fatalf("Unexpected error: %v", err)
591 }
592 err = filterV4.AddIPNet(ConntrackOrigDstIP, ipNet)
593 if err != nil {
594 t.Fatalf("Unexpected error: %v", err)
595 }
596
597 filterV6 = &ConntrackFilter{}
598 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
599 if err != nil {
600 t.Fatalf("Unexpected error: %v", err)
601 }
602 err = filterV6.AddIPNet(ConntrackOrigDstIP, ipNet)
603 if err != nil {
604 t.Fatalf("Unexpected error: %v", err)
605 }
606
607 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
608 if v4Match != 2 || v6Match != 1 {
609 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
610 }
611
612
613 filterV4 = &ConntrackFilter{}
614 ipNet, err = ParseIPNet("20.0.0.0/12")
615 if err != nil {
616 t.Fatalf("Unexpected error: %v", err)
617 }
618 err = filterV4.AddIPNet(ConntrackReplySrcIP, ipNet)
619 if err != nil {
620 t.Fatalf("Unexpected error: %v", err)
621 }
622
623 filterV6 = &ConntrackFilter{}
624 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
625 if err != nil {
626 t.Fatalf("Unexpected error: %v", err)
627 }
628 err = filterV6.AddIPNet(ConntrackReplySrcIP, ipNet)
629 if err != nil {
630 t.Fatalf("Unexpected error: %v", err)
631 }
632
633 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
634 if v4Match != 2 || v6Match != 1 {
635 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
636 }
637
638
639 filterV4 = &ConntrackFilter{}
640 ipNet, err = ParseIPNet("192.168.0.0/12")
641 if err != nil {
642 t.Fatalf("Unexpected error: %v", err)
643 }
644 err = filterV4.AddIPNet(ConntrackReplyDstIP, ipNet)
645 if err != nil {
646 t.Fatalf("Unexpected error: %v", err)
647 }
648
649 filterV6 = &ConntrackFilter{}
650 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
651 if err != nil {
652 t.Fatalf("Unexpected error: %v", err)
653 }
654 err = filterV6.AddIPNet(ConntrackReplyDstIP, ipNet)
655 if err != nil {
656 t.Fatalf("Unexpected error: %v", err)
657 }
658
659 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
660 if v4Match != 2 || v6Match != 0 {
661 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
662 }
663
664
665 filterV4 = &ConntrackFilter{}
666 ipNet, err = ParseIPNet("192.168.0.0/12")
667 if err != nil {
668 t.Fatalf("Unexpected error: %v", err)
669 }
670 err = filterV4.AddIPNet(ConntrackReplyAnyIP, ipNet)
671 if err != nil {
672 t.Fatalf("Unexpected error: %v", err)
673 }
674
675 filterV6 = &ConntrackFilter{}
676 ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
677 if err != nil {
678 t.Fatalf("Unexpected error: %v", err)
679 }
680 err = filterV6.AddIPNet(ConntrackReplyAnyIP, ipNet)
681 if err != nil {
682 t.Fatalf("Unexpected error: %v", err)
683 }
684
685 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
686 if v4Match != 2 || v6Match != 1 {
687 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
688 }
689
690 filterV4 = &ConntrackFilter{}
691 err = filterV4.AddProtocol(6)
692 if err != nil {
693 t.Fatalf("Unexpected error: %v", err)
694 }
695 err = filterV4.AddPort(ConntrackOrigSrcPort, 5000)
696 if err != nil {
697 t.Fatalf("Unexpected error: %v", err)
698 }
699
700 filterV6 = &ConntrackFilter{}
701 err = filterV6.AddProtocol(132)
702 if err != nil {
703 t.Fatalf("Unexpected error: %v", err)
704 }
705 err = filterV6.AddPort(ConntrackOrigSrcPort, 1000)
706 if err != nil {
707 t.Fatalf("Unexpected error: %v", err)
708 }
709
710 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
711 if v4Match != 1 || v6Match != 1 {
712 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
713 }
714
715
716 filterV4 = &ConntrackFilter{}
717 err = filterV4.AddProtocol(6)
718 if err != nil {
719 t.Fatalf("Unexpected error: %v", err)
720 }
721 err = filterV4.AddPort(ConntrackOrigDstPort, 6000)
722 if err != nil {
723 t.Fatalf("Unexpected error: %v", err)
724 }
725
726 filterV6 = &ConntrackFilter{}
727 err = filterV6.AddProtocol(132)
728 if err != nil {
729 t.Fatalf("Unexpected error: %v", err)
730 }
731 err = filterV6.AddPort(ConntrackOrigDstPort, 2000)
732 if err != nil {
733 t.Fatalf("Unexpected error: %v", err)
734 }
735
736 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
737 if v4Match != 1 || v6Match != 1 {
738 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
739 }
740
741
742 filterV4 = &ConntrackFilter{}
743 var labels [][]byte
744 labels = append(labels, []byte{3, 4, 61, 141, 207, 170})
745 labels = append(labels, []byte{0x2})
746 err = filterV4.AddLabels(ConntrackMatchLabels, labels)
747 if err != nil {
748 t.Fatalf("Unexpected error: %v", err)
749 }
750
751 filterV6 = &ConntrackFilter{}
752 err = filterV6.AddLabels(ConntrackUnmatchLabels, labels)
753 if err != nil {
754 t.Fatalf("Unexpected error: %v", err)
755 }
756
757 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
758 if v4Match != 1 || v6Match != 0 {
759 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
760 }
761
762 filterV4 = &ConntrackFilter{}
763 err = filterV4.AddZone(200)
764 if err != nil {
765 t.Fatalf("Unexpected error: %v", err)
766 }
767 filterV6 = &ConntrackFilter{}
768 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
769 if v4Match != 2 || v6Match != 0 {
770 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
771 }
772 }
773
774 func TestParseRawData(t *testing.T) {
775 if nl.NativeEndian() == binary.BigEndian {
776 t.Skip("testdata expect little-endian test executor")
777 }
778 os.Setenv("TZ", "")
779 tests := []struct {
780 testname string
781 rawData []byte
782 expConntrackFlow string
783 }{
784 {
785 testname: "UDP conntrack",
786 rawData: []byte{
787
788 2, 0, 0, 0,
789
790 52, 0, 1, 128,
791
792 20, 0, 1, 128,
793
794 8, 0, 1, 0,
795 192, 168, 0, 10,
796
797 8, 0, 2, 0,
798 192, 168, 0, 3,
799
800 28, 0, 2, 128,
801
802 5, 0, 1, 0,
803 17, 0, 0, 0,
804
805 6, 0, 2, 0,
806 189, 1, 0, 0,
807
808 6, 0, 3, 0,
809 0, 53, 0, 0,
810
811 52, 0, 2, 128,
812
813 20, 0, 1, 128,
814
815 8, 0, 1, 0,
816 192, 168, 0, 3,
817
818 8, 0, 2, 0,
819 192, 168, 0, 10,
820
821 28, 0, 2, 128,
822
823 5, 0, 1, 0,
824 17, 0, 0, 0,
825
826 6, 0, 2, 0,
827 0, 53, 0, 0,
828
829 6, 0, 3, 0,
830 189, 1, 0, 0,
831
832 8, 0, 3, 0,
833 0, 0, 1, 138,
834
835 8, 0, 8, 0,
836 0, 0, 0, 5,
837
838 8, 0, 12, 0,
839 81, 172, 253, 151,
840
841 8, 0, 11, 0,
842 0, 0, 0, 1,
843
844 8, 0, 7, 0,
845 0, 0, 0, 32,
846
847 28, 0, 9, 128,
848
849 12, 0, 1, 0,
850 0, 0, 0, 0, 0, 0, 0, 1,
851
852 12, 0, 2, 0,
853 0, 0, 0, 0, 0, 0, 0, 55,
854
855 28, 0, 10, 128,
856
857 12, 0, 1, 0,
858 0, 0, 0, 0, 0, 0, 0, 1,
859
860 12, 0, 2, 0,
861 0, 0, 0, 0, 0, 0, 0, 71,
862
863 16, 0, 20, 128,
864
865 12, 0, 1, 0,
866 22, 134, 80, 142, 230, 127, 74, 166,
867
868 20, 0, 22, 0,
869 0, 0, 0, 0, 5, 0, 18, 172, 66, 2, 1, 0, 0, 0, 0, 0},
870 expConntrackFlow: "udp\t17 src=192.168.0.10 dst=192.168.0.3 sport=48385 dport=53 packets=1 bytes=55\t" +
871 "src=192.168.0.3 dst=192.168.0.10 sport=53 dport=48385 packets=1 bytes=71 mark=0x5 labels=0x00000000050012ac4202010000000000 " +
872 "start=2021-06-07 13:41:30.39632247 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=32(sec)",
873 },
874 {
875 testname: "TCP conntrack",
876 rawData: []byte{
877
878 2, 0, 0, 0,
879
880 52, 0, 1, 128,
881
882 20, 0, 1, 128,
883
884 8, 0, 1, 0,
885 192, 168, 0, 10,
886
887 8, 0, 2, 0,
888 192, 168, 77, 73,
889
890 28, 0, 2, 128,
891
892 5, 0, 1, 0,
893 6, 0, 0, 0,
894
895 6, 0, 2, 0,
896 166, 129, 0, 0,
897
898 6, 0, 3, 0,
899 13, 5, 0, 0,
900
901 52, 0, 2, 128,
902
903 20, 0, 1, 128,
904
905 8, 0, 1, 0,
906 192, 168, 77, 73,
907
908 8, 0, 2, 0,
909 192, 168, 0, 10,
910
911 28, 0, 2, 128,
912
913 5, 0, 1, 0,
914 6, 0, 0, 0,
915
916 6, 0, 2, 0,
917 13, 5, 0, 0,
918
919 6, 0, 3, 0,
920 166, 129, 0, 0,
921
922 8, 0, 3, 0,
923 0, 0, 1, 142,
924
925 8, 0, 8, 0,
926 0, 0, 0, 5,
927
928 8, 0, 12, 0,
929 177, 65, 179, 133,
930
931 8, 0, 11, 0,
932 0, 0, 0, 1,
933
934 8, 0, 7, 0,
935 0, 0, 0, 152,
936
937 48, 0, 4, 128,
938 44, 0, 1, 128,
939 5, 0, 1, 0, 8, 0, 0, 0,
940 5, 0, 2, 0, 0, 0, 0, 0,
941 5, 0, 3, 0, 0, 0, 0, 0,
942 6, 0, 4, 0, 39, 0, 0, 0,
943 6, 0, 5, 0, 32, 0, 0, 0,
944
945 28, 0, 9, 128,
946
947 12, 0, 1, 0,
948 0, 0, 0, 0, 0, 0, 0, 11,
949
950 12, 0, 2, 0,
951 0, 0, 0, 0, 0, 0, 7, 122,
952
953 28, 0, 10, 128,
954
955 12, 0, 1, 0,
956 0, 0, 0, 0, 0, 0, 0, 10,
957
958 12, 0, 2, 0,
959 0, 0, 0, 0, 0, 0, 7, 66,
960
961 8, 0, 18, 0,
962 0, 100, 0, 0,
963
964 16, 0, 20, 128,
965
966 12, 0, 1, 0,
967 22, 134, 80, 175, 134, 10, 182, 221},
968 expConntrackFlow: "tcp\t6 src=192.168.0.10 dst=192.168.77.73 sport=42625 dport=3333 packets=11 bytes=1914\t" +
969 "src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 zone=100 " +
970 "start=2021-06-07 13:43:50.511990493 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=152(sec)",
971 },
972 }
973
974 for _, test := range tests {
975 t.Run(test.testname, func(t *testing.T) {
976 conntrackFlow := parseRawData(test.rawData)
977 if conntrackFlow.String() != test.expConntrackFlow {
978 t.Errorf("expected conntrack flow:\n\t%q\ngot conntrack flow:\n\t%q",
979 test.expConntrackFlow, conntrackFlow)
980 }
981 })
982 }
983 }
984
985
986
987 func TestConntrackUpdateV4(t *testing.T) {
988
989 os.Setenv("TZ", "")
990
991 requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
992 k, m, err := KernelVersion()
993 if err != nil {
994 t.Fatal(err)
995 }
996
997
998 if k < 4 || k == 4 && m < 19 {
999 requiredModules = append(requiredModules, "nf_conntrack_ipv4")
1000 }
1001
1002 nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
1003 defer teardown()
1004
1005 ns, err := netns.GetFromName(nsStr)
1006 if err != nil {
1007 t.Fatalf("couldn't get handle to generated namespace: %s", err)
1008 }
1009
1010 h, err := NewHandleAt(ns, nl.FAMILY_V4)
1011 if err != nil {
1012 t.Fatalf("failed to create netlink handle: %s", err)
1013 }
1014
1015 flow := ConntrackFlow{
1016 FamilyType: FAMILY_V4,
1017 Forward: IPTuple{
1018 SrcIP: net.IP{234,234,234,234},
1019 DstIP: net.IP{123,123,123,123},
1020 SrcPort: 48385,
1021 DstPort: 53,
1022 Protocol: unix.IPPROTO_TCP,
1023 },
1024 Reverse: IPTuple{
1025 SrcIP: net.IP{123,123,123,123},
1026 DstIP: net.IP{234,234,234,234},
1027 SrcPort: 53,
1028 DstPort: 48385,
1029 Protocol: unix.IPPROTO_TCP,
1030 },
1031
1032
1033 TimeOut: 100,
1034 Mark: 12,
1035 ProtoInfo: &ProtoInfoTCP{
1036 State: nl.TCP_CONNTRACK_SYN_SENT2,
1037 },
1038 }
1039
1040 err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V4, &flow)
1041 if err == nil {
1042 t.Fatalf("expected an error to occur when trying to update a non-existant conntrack: %+v", flow)
1043 }
1044
1045 err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V4, &flow)
1046 if err != nil {
1047 t.Fatalf("failed to insert conntrack: %s", err)
1048 }
1049
1050 flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
1051 if err != nil {
1052 t.Fatalf("failed to list conntracks following successful insert: %s", err)
1053 }
1054
1055 filter := ConntrackFilter{
1056 ipNetFilter: map[ConntrackFilterType]*net.IPNet{
1057 ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
1058 ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
1059 ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
1060 ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
1061 },
1062 portFilter: map[ConntrackFilterType]uint16{
1063 ConntrackOrigSrcPort: flow.Forward.SrcPort,
1064 ConntrackOrigDstPort: flow.Forward.DstPort,
1065 },
1066 protoFilter:unix.IPPROTO_TCP,
1067 }
1068
1069 var match *ConntrackFlow
1070 for _, f := range flows {
1071 if filter.MatchConntrackFlow(f) {
1072 match = f
1073 break
1074 }
1075 }
1076
1077 if match == nil {
1078 t.Fatalf("Didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
1079 } else {
1080 t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
1081 }
1082 checkFlowsEqual(t, &flow, match)
1083 checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
1084
1085
1086 flow.Mark = 10
1087 flow.ProtoInfo = &ProtoInfoTCP{
1088 State: nl.TCP_CONNTRACK_ESTABLISHED,
1089 }
1090 err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V4, &flow)
1091 if err != nil {
1092 t.Fatalf("failed to update conntrack with new mark: %s", err)
1093 }
1094
1095
1096 flows, err = h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
1097 if err != nil {
1098 t.Fatalf("failed to list conntracks following successful update: %s", err)
1099 }
1100
1101 var updatedMatch *ConntrackFlow
1102 for _, f := range flows {
1103 if filter.MatchConntrackFlow(f) {
1104 updatedMatch = f
1105 break
1106 }
1107 }
1108 if updatedMatch == nil {
1109 t.Fatalf("Didn't find any matching conntrack entries for updated flow: %+v\n Filter used: %+v", flow, filter)
1110 } else {
1111 t.Logf("Found entry in conntrack table matching updated flow: %+v labels=%+v", updatedMatch, updatedMatch.Labels)
1112 }
1113
1114 checkFlowsEqual(t, &flow, updatedMatch)
1115 checkProtoInfosEqual(t, flow.ProtoInfo, updatedMatch.ProtoInfo)
1116 }
1117
1118
1119
1120 func TestConntrackUpdateV6(t *testing.T) {
1121
1122 os.Setenv("TZ", "")
1123
1124 requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
1125 k, m, err := KernelVersion()
1126 if err != nil {
1127 t.Fatal(err)
1128 }
1129
1130
1131 if k < 4 || k == 4 && m < 19 {
1132 requiredModules = append(requiredModules, "nf_conntrack_ipv4")
1133 }
1134
1135 nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
1136 defer teardown()
1137
1138 ns, err := netns.GetFromName(nsStr)
1139 if err != nil {
1140 t.Fatalf("couldn't get handle to generated namespace: %s", err)
1141 }
1142
1143 h, err := NewHandleAt(ns, nl.FAMILY_V6)
1144 if err != nil {
1145 t.Fatalf("failed to create netlink handle: %s", err)
1146 }
1147
1148 flow := ConntrackFlow{
1149 FamilyType: FAMILY_V6,
1150 Forward: IPTuple{
1151 SrcIP: net.ParseIP("2001:db8::68"),
1152 DstIP: net.ParseIP("2001:db9::32"),
1153 SrcPort: 48385,
1154 DstPort: 53,
1155 Protocol: unix.IPPROTO_TCP,
1156 },
1157 Reverse: IPTuple{
1158 SrcIP: net.ParseIP("2001:db9::32"),
1159 DstIP: net.ParseIP("2001:db8::68"),
1160 SrcPort: 53,
1161 DstPort: 48385,
1162 Protocol: unix.IPPROTO_TCP,
1163 },
1164
1165
1166 TimeOut: 100,
1167 Mark: 12,
1168 ProtoInfo: &ProtoInfoTCP{
1169 State: nl.TCP_CONNTRACK_SYN_SENT2,
1170 },
1171 }
1172
1173 err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V6, &flow)
1174 if err == nil {
1175 t.Fatalf("expected an error to occur when trying to update a non-existant conntrack: %+v", flow)
1176 }
1177
1178 err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V6, &flow)
1179 if err != nil {
1180 t.Fatalf("failed to insert conntrack: %s", err)
1181 }
1182
1183 flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
1184 if err != nil {
1185 t.Fatalf("failed to list conntracks following successful insert: %s", err)
1186 }
1187
1188 filter := ConntrackFilter{
1189 ipNetFilter: map[ConntrackFilterType]*net.IPNet{
1190 ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
1191 ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
1192 ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
1193 ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
1194 },
1195 portFilter: map[ConntrackFilterType]uint16{
1196 ConntrackOrigSrcPort: flow.Forward.SrcPort,
1197 ConntrackOrigDstPort: flow.Forward.DstPort,
1198 },
1199 protoFilter:unix.IPPROTO_TCP,
1200 }
1201
1202 var match *ConntrackFlow
1203 for _, f := range flows {
1204 if filter.MatchConntrackFlow(f) {
1205 match = f
1206 break
1207 }
1208 }
1209
1210 if match == nil {
1211 t.Fatalf("didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
1212 } else {
1213 t.Logf("found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
1214 }
1215 checkFlowsEqual(t, &flow, match)
1216 checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
1217
1218
1219 flow.Mark = 10
1220 flow.ProtoInfo = &ProtoInfoTCP{
1221 State: nl.TCP_CONNTRACK_ESTABLISHED,
1222 }
1223 err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V6, &flow)
1224 if err != nil {
1225 t.Fatalf("failed to update conntrack with new mark: %s", err)
1226 }
1227
1228
1229 flows, err = h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
1230 if err != nil {
1231 t.Fatalf("failed to list conntracks following successful update: %s", err)
1232 }
1233
1234 var updatedMatch *ConntrackFlow
1235 for _, f := range flows {
1236 if filter.MatchConntrackFlow(f) {
1237 updatedMatch = f
1238 break
1239 }
1240 }
1241 if updatedMatch == nil {
1242 t.Fatalf("didn't find any matching conntrack entries for updated flow: %+v\n Filter used: %+v", flow, filter)
1243 } else {
1244 t.Logf("found entry in conntrack table matching updated flow: %+v labels=%+v", updatedMatch, updatedMatch.Labels)
1245 }
1246
1247 checkFlowsEqual(t, &flow, updatedMatch)
1248 checkProtoInfosEqual(t, flow.ProtoInfo, updatedMatch.ProtoInfo)
1249 }
1250
1251 func TestConntrackCreateV4(t *testing.T) {
1252
1253 os.Setenv("TZ", "")
1254
1255 requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
1256 k, m, err := KernelVersion()
1257 if err != nil {
1258 t.Fatal(err)
1259 }
1260
1261
1262 if k < 4 || k == 4 && m < 19 {
1263 requiredModules = append(requiredModules, "nf_conntrack_ipv4")
1264 }
1265
1266 nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
1267 defer teardown()
1268
1269 ns, err := netns.GetFromName(nsStr)
1270 if err != nil {
1271 t.Fatalf("couldn't get handle to generated namespace: %s", err)
1272 }
1273
1274 h, err := NewHandleAt(ns, nl.FAMILY_V4)
1275 if err != nil {
1276 t.Fatalf("failed to create netlink handle: %s", err)
1277 }
1278
1279 flow := ConntrackFlow{
1280 FamilyType: FAMILY_V4,
1281 Forward: IPTuple{
1282 SrcIP: net.IP{234,234,234,234},
1283 DstIP: net.IP{123,123,123,123},
1284 SrcPort: 48385,
1285 DstPort: 53,
1286 Protocol: unix.IPPROTO_TCP,
1287 },
1288 Reverse: IPTuple{
1289 SrcIP: net.IP{123,123,123,123},
1290 DstIP: net.IP{234,234,234,234},
1291 SrcPort: 53,
1292 DstPort: 48385,
1293 Protocol: unix.IPPROTO_TCP,
1294 },
1295
1296
1297 TimeOut: 100,
1298 Mark: 12,
1299 ProtoInfo: &ProtoInfoTCP{
1300 State: nl.TCP_CONNTRACK_ESTABLISHED,
1301 },
1302 }
1303
1304 err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V4, &flow)
1305 if err != nil {
1306 t.Fatalf("failed to insert conntrack: %s", err)
1307 }
1308
1309 flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
1310 if err != nil {
1311 t.Fatalf("failed to list conntracks following successful insert: %s", err)
1312 }
1313
1314 filter := ConntrackFilter{
1315 ipNetFilter: map[ConntrackFilterType]*net.IPNet{
1316 ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
1317 ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
1318 ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
1319 ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
1320 },
1321 portFilter: map[ConntrackFilterType]uint16{
1322 ConntrackOrigSrcPort: flow.Forward.SrcPort,
1323 ConntrackOrigDstPort: flow.Forward.DstPort,
1324 },
1325 protoFilter:unix.IPPROTO_TCP,
1326 }
1327
1328 var match *ConntrackFlow
1329 for _, f := range flows {
1330 if filter.MatchConntrackFlow(f) {
1331 match = f
1332 break
1333 }
1334 }
1335
1336 if match == nil {
1337 t.Fatalf("didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
1338 } else {
1339 t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
1340 }
1341
1342 checkFlowsEqual(t, &flow, match)
1343 checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
1344 }
1345
1346 func TestConntrackCreateV6(t *testing.T) {
1347
1348 os.Setenv("TZ", "")
1349
1350 requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
1351 k, m, err := KernelVersion()
1352 if err != nil {
1353 t.Fatal(err)
1354 }
1355
1356
1357 if k < 4 || k == 4 && m < 19 {
1358 requiredModules = append(requiredModules, "nf_conntrack_ipv4")
1359 }
1360
1361 nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
1362 defer teardown()
1363
1364 ns, err := netns.GetFromName(nsStr)
1365 if err != nil {
1366 t.Fatalf("couldn't get handle to generated namespace: %s", err)
1367 }
1368
1369 h, err := NewHandleAt(ns, nl.FAMILY_V6)
1370 if err != nil {
1371 t.Fatalf("failed to create netlink handle: %s", err)
1372 }
1373
1374 flow := ConntrackFlow{
1375 FamilyType: FAMILY_V6,
1376 Forward: IPTuple{
1377 SrcIP: net.ParseIP("2001:db8::68"),
1378 DstIP: net.ParseIP("2001:db9::32"),
1379 SrcPort: 48385,
1380 DstPort: 53,
1381 Protocol: unix.IPPROTO_TCP,
1382 },
1383 Reverse: IPTuple{
1384 SrcIP: net.ParseIP("2001:db9::32"),
1385 DstIP: net.ParseIP("2001:db8::68"),
1386 SrcPort: 53,
1387 DstPort: 48385,
1388 Protocol: unix.IPPROTO_TCP,
1389 },
1390
1391
1392 TimeOut: 100,
1393 Mark: 12,
1394 ProtoInfo: &ProtoInfoTCP{
1395 State: nl.TCP_CONNTRACK_ESTABLISHED,
1396 },
1397 }
1398
1399 err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V6, &flow)
1400 if err != nil {
1401 t.Fatalf("failed to insert conntrack: %s", err)
1402 }
1403
1404 flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
1405 if err != nil {
1406 t.Fatalf("failed to list conntracks following successful insert: %s", err)
1407 }
1408
1409 filter := ConntrackFilter{
1410 ipNetFilter: map[ConntrackFilterType]*net.IPNet{
1411 ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
1412 ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
1413 ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
1414 ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
1415 },
1416 portFilter: map[ConntrackFilterType]uint16{
1417 ConntrackOrigSrcPort: flow.Forward.SrcPort,
1418 ConntrackOrigDstPort: flow.Forward.DstPort,
1419 },
1420 protoFilter:unix.IPPROTO_TCP,
1421 }
1422
1423 var match *ConntrackFlow
1424 for _, f := range flows {
1425 if filter.MatchConntrackFlow(f) {
1426 match = f
1427 break
1428 }
1429 }
1430
1431 if match == nil {
1432 t.Fatalf("Didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
1433 } else {
1434 t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
1435 }
1436
1437
1438 if match.Mark != flow.Mark {
1439 t.Logf("Matched kernel entry did not have correct mark. Kernel: %d, Expected: %d", flow.Mark, match.Mark)
1440 t.Fail()
1441 }
1442 checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
1443 }
1444
1445
1446
1447 func TestConntrackFlowToNlData(t *testing.T) {
1448 flowV4 := ConntrackFlow{
1449 FamilyType: FAMILY_V4,
1450 Forward: IPTuple{
1451 SrcIP: net.IP{234,234,234,234},
1452 DstIP: net.IP{123,123,123,123},
1453 SrcPort: 48385,
1454 DstPort: 53,
1455 Protocol: unix.IPPROTO_TCP,
1456 },
1457 Reverse: IPTuple{
1458 SrcIP: net.IP{123,123,123,123},
1459 DstIP: net.IP{234,234,234,234},
1460 SrcPort: 53,
1461 DstPort: 48385,
1462 Protocol: unix.IPPROTO_TCP,
1463 },
1464 Mark: 5,
1465 TimeOut: 10,
1466 ProtoInfo: &ProtoInfoTCP{
1467 State: nl.TCP_CONNTRACK_ESTABLISHED,
1468 },
1469 }
1470 flowV6 := ConntrackFlow {
1471 FamilyType: FAMILY_V6,
1472 Forward: IPTuple{
1473 SrcIP: net.ParseIP("2001:db8::68"),
1474 DstIP: net.ParseIP("2001:db9::32"),
1475 SrcPort: 48385,
1476 DstPort: 53,
1477 Protocol: unix.IPPROTO_TCP,
1478 },
1479 Reverse: IPTuple{
1480 SrcIP: net.ParseIP("2001:db9::32"),
1481 DstIP: net.ParseIP("2001:db8::68"),
1482 SrcPort: 53,
1483 DstPort: 48385,
1484 Protocol: unix.IPPROTO_TCP,
1485 },
1486 Mark: 5,
1487 TimeOut: 10,
1488 ProtoInfo: &ProtoInfoTCP{
1489 State: nl.TCP_CONNTRACK_ESTABLISHED,
1490 },
1491 }
1492
1493 var bytesV4, bytesV6 []byte
1494
1495 attrsV4, err := flowV4.toNlData()
1496 if err != nil {
1497 t.Fatalf("Error converting ConntrackFlow to netlink messages: %s", err)
1498 }
1499
1500 bytesV4 = append(bytesV4, flowV4.FamilyType,0,0,0)
1501 for _, a := range attrsV4 {
1502 bytesV4 = append(bytesV4, a.Serialize()...)
1503 }
1504
1505 attrsV6, err := flowV6.toNlData()
1506 if err != nil {
1507 t.Fatalf("Error converting ConntrackFlow to netlink messages: %s", err)
1508 }
1509
1510 bytesV6 = append(bytesV6, flowV6.FamilyType,0,0,0)
1511 for _, a := range attrsV6 {
1512 bytesV6 = append(bytesV6, a.Serialize()...)
1513 }
1514
1515 parsedFlowV4 := parseRawData(bytesV4)
1516 checkFlowsEqual(t, &flowV4, parsedFlowV4)
1517 checkProtoInfosEqual(t, flowV4.ProtoInfo, parsedFlowV4.ProtoInfo)
1518
1519 parsedFlowV6 := parseRawData(bytesV6)
1520 checkFlowsEqual(t, &flowV6, parsedFlowV6)
1521 checkProtoInfosEqual(t, flowV6.ProtoInfo, parsedFlowV6.ProtoInfo)
1522 }
1523
1524 func checkFlowsEqual(t *testing.T, f1, f2 *ConntrackFlow) {
1525
1526
1527 if f1.FamilyType != f2.FamilyType {
1528 t.Logf("Conntrack flow FamilyTypes differ. Tuple1: %d, Tuple2: %d.\n", f1.FamilyType, f2.FamilyType)
1529 t.Fail()
1530 }
1531 if f1.Mark != f2.Mark {
1532 t.Logf("Conntrack flow Marks differ. Tuple1: %d, Tuple2: %d.\n", f1.Mark, f2.Mark)
1533 t.Fail()
1534 }
1535 if !tuplesEqual(f1.Forward, f2.Forward) {
1536 t.Logf("Forward tuples mismatch. Tuple1 forward flow: %+v, Tuple2 forward flow: %+v.\n", f1.Forward, f2.Forward)
1537 t.Fail()
1538 }
1539 if !tuplesEqual(f1.Reverse, f2.Reverse) {
1540 t.Logf("Reverse tuples mismatch. Tuple1 reverse flow: %+v, Tuple2 reverse flow: %+v.\n", f1.Reverse, f2.Reverse)
1541 t.Fail()
1542 }
1543 }
1544
1545 func checkProtoInfosEqual(t *testing.T, p1, p2 ProtoInfo) {
1546 t.Logf("Checking protoinfo fields equal:\n\t p1: %+v\n\t p2: %+v", p1, p2)
1547 if !protoInfosEqual(p1, p2) {
1548 t.Logf("Protoinfo structs differ: P1: %+v, P2: %+v", p1, p2)
1549 t.Fail()
1550 }
1551 }
1552
1553 func protoInfosEqual(p1, p2 ProtoInfo) bool {
1554 if p1 == nil {
1555 return p2 == nil
1556 } else if p2 != nil {
1557 return p1.Protocol() == p2.Protocol()
1558 }
1559
1560 return false
1561 }
1562
1563 func tuplesEqual(t1, t2 IPTuple) bool {
1564 if t1.Bytes != t2.Bytes {
1565 return false
1566 }
1567
1568 if !t1.DstIP.Equal(t2.DstIP) {
1569 return false
1570 }
1571
1572 if !t1.SrcIP.Equal(t2.SrcIP) {
1573 return false
1574 }
1575
1576 if t1.DstPort != t2.DstPort {
1577 return false
1578 }
1579
1580 if t1.SrcPort != t2.SrcPort {
1581 return false
1582 }
1583
1584 if t1.Packets != t2.Packets {
1585 return false
1586 }
1587
1588 if t1.Protocol != t2.Protocol {
1589 return false
1590 }
1591
1592 return true
1593 }
1594
View as plain text