github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/conntrack_test.go (about) 1 //go:build linux 2 // +build linux 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/sagernet/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 // Lock the OS Thread so we don't accidentally switch namespaces 49 runtime.LockOSThread() 50 51 // Save the current network namespace 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 // Enter the new namespace 61 netns.Set(ns) 62 63 // Bing up loopback 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 // TestConntrackSocket test the opening of a NETFILTER family socket 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 // TestConntrackTableList test the conntrack table list 97 // Creates some flows and checks that they are correctly fetched from the conntrack table 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 // conntrack l3proto was unified since 4.19 108 // https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f 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 // Creates a new namespace and bring up the loopback interface 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 // Flush the table to start fresh 128 err = h.ConntrackTableFlush(ConntrackTable) 129 CheckErrorFail(t, err) 130 131 // Create 5 udp 132 startTime := time.Now() 133 udpFlowCreateProg(t, 5, 2000, "127.0.0.10", 3000) 134 135 // Fetch the conntrack table 136 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET) 137 CheckErrorFail(t, err) 138 139 // Check that it is able to find the 5 flows created 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 // Expect at most one second to have already passed from the configured UDP timeout of 45secs. 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 // Give a try also to the IPv6 version 169 _, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET6) 170 CheckErrorFail(t, err) 171 172 // Switch back to the original namespace 173 netns.Set(*origns) 174 } 175 176 // TestConntrackTableFlush test the conntrack table flushing 177 // Creates some flows and then call the table flush 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 // conntrack l3proto was unified since 4.19 190 // https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f 191 if k < 4 || k == 4 && m < 19 { 192 setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4") 193 } 194 setUpNetlinkTestWithKModule(t, "nf_conntrack") 195 // Creates a new namespace and bring up the loopback interface 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 // Create 5 udp flows using netcat 203 udpFlowCreateProg(t, 5, 3000, "127.0.0.10", 4000) 204 205 // Fetch the conntrack table 206 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET) 207 CheckErrorFail(t, err) 208 209 // Check that it is able to find the 5 flows created 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 // Flush the table 224 err = h.ConntrackTableFlush(ConntrackTable) 225 CheckErrorFail(t, err) 226 227 // Fetch again the flows to validate the flush 228 flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET) 229 CheckErrorFail(t, err) 230 231 // Check if it is still able to find the 5 flows created 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 // Switch back to the original namespace 246 netns.Set(*origns) 247 } 248 249 // TestConntrackTableDelete tests the deletion with filter 250 // Creates 2 group of flows then deletes only one group and validates the result 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 setUpNetlinkTestWithKModule(t, "nf_conntrack") 257 setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink") 258 k, m, err := KernelVersion() 259 if err != nil { 260 t.Fatal(err) 261 } 262 // conntrack l3proto was unified since 4.19 263 // https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f 264 if k < 4 || k == 4 && m < 19 { 265 setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4") 266 } 267 268 // Creates a new namespace and bring up the loopback interface 269 origns, ns, h := nsCreateAndEnter(t) 270 defer netns.Set(*origns) 271 defer origns.Close() 272 defer ns.Close() 273 defer runtime.UnlockOSThread() 274 275 // Create 10 udp flows 276 udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000) 277 udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000) 278 279 // Fetch the conntrack table 280 flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET) 281 CheckErrorFail(t, err) 282 283 // Check that it is able to find the 5 flows created for each group 284 var groupA int 285 var groupB int 286 for _, flow := range flows { 287 if flow.Forward.Protocol == 17 && 288 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && 289 flow.Forward.DstPort == 6000 && 290 (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) { 291 groupA++ 292 } 293 if flow.Forward.Protocol == 17 && 294 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) && 295 flow.Forward.DstPort == 8000 && 296 (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) { 297 groupB++ 298 } 299 } 300 if groupA != 5 || groupB != 5 { 301 t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB) 302 } 303 304 // Create a filter to erase groupB flows 305 filter := &ConntrackFilter{} 306 filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20")) 307 filter.AddProtocol(17) 308 filter.AddPort(ConntrackOrigDstPort, 8000) 309 310 // Flush entries of groupB 311 var deleted uint 312 if deleted, err = h.ConntrackDeleteFilter(ConntrackTable, unix.AF_INET, filter); err != nil { 313 t.Fatalf("Error during the erase: %s", err) 314 } 315 if deleted != 5 { 316 t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted) 317 } 318 319 // Check again the table to verify that are gone 320 flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET) 321 CheckErrorFail(t, err) 322 323 // Check if it is able to find the 5 flows of groupA but none of groupB 324 groupA = 0 325 groupB = 0 326 for _, flow := range flows { 327 if flow.Forward.Protocol == 17 && 328 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && 329 flow.Forward.DstPort == 6000 && 330 (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) { 331 groupA++ 332 } 333 if flow.Forward.Protocol == 17 && 334 flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) && 335 flow.Forward.DstPort == 8000 && 336 (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) { 337 groupB++ 338 } 339 } 340 if groupA != 5 || groupB > 0 { 341 t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB) 342 } 343 344 // Switch back to the original namespace 345 netns.Set(*origns) 346 } 347 348 func TestConntrackFilter(t *testing.T) { 349 var flowList []ConntrackFlow 350 flowList = append(flowList, ConntrackFlow{ 351 FamilyType: unix.AF_INET, 352 Forward: ipTuple{ 353 SrcIP: net.ParseIP("10.0.0.1"), 354 DstIP: net.ParseIP("20.0.0.1"), 355 SrcPort: 1000, 356 DstPort: 2000, 357 Protocol: 17, 358 }, 359 Reverse: ipTuple{ 360 SrcIP: net.ParseIP("20.0.0.1"), 361 DstIP: net.ParseIP("192.168.1.1"), 362 SrcPort: 2000, 363 DstPort: 1000, 364 Protocol: 17, 365 }, 366 }, 367 ConntrackFlow{ 368 FamilyType: unix.AF_INET, 369 Forward: ipTuple{ 370 SrcIP: net.ParseIP("10.0.0.2"), 371 DstIP: net.ParseIP("20.0.0.2"), 372 SrcPort: 5000, 373 DstPort: 6000, 374 Protocol: 6, 375 }, 376 Reverse: ipTuple{ 377 SrcIP: net.ParseIP("20.0.0.2"), 378 DstIP: net.ParseIP("192.168.1.1"), 379 SrcPort: 6000, 380 DstPort: 5000, 381 Protocol: 6, 382 }, 383 }, 384 ConntrackFlow{ 385 FamilyType: unix.AF_INET6, 386 Forward: ipTuple{ 387 SrcIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), 388 DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), 389 SrcPort: 1000, 390 DstPort: 2000, 391 Protocol: 132, 392 }, 393 Reverse: ipTuple{ 394 SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), 395 DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), 396 SrcPort: 2000, 397 DstPort: 1000, 398 Protocol: 132, 399 }, 400 }) 401 402 // Empty filter 403 v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{}) 404 if v4Match > 0 || v6Match > 0 { 405 t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match) 406 } 407 408 // Filter errors 409 410 // Adding same attribute should fail 411 filter := &ConntrackFilter{} 412 err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")) 413 if err != nil { 414 t.Fatalf("Unexpected error: %v", err) 415 } 416 if err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")); err == nil { 417 t.Fatalf("Error, it should fail adding same attribute to the filter") 418 } 419 err = filter.AddProtocol(6) 420 if err != nil { 421 t.Fatalf("Unexpected error: %v", err) 422 } 423 if err := filter.AddProtocol(17); err == nil { 424 t.Fatalf("Error, it should fail adding same attribute to the filter") 425 } 426 filter.AddPort(ConntrackOrigSrcPort, 80) 427 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil { 428 t.Fatalf("Error, it should fail adding same attribute to the filter") 429 } 430 431 // Can not add a Port filter without Layer 4 protocol 432 filter = &ConntrackFilter{} 433 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil { 434 t.Fatalf("Error, it should fail adding a port filter without a protocol") 435 } 436 437 // Can not add a Port filter if the Layer 4 protocol does not support it 438 filter = &ConntrackFilter{} 439 err = filter.AddProtocol(47) 440 if err != nil { 441 t.Fatalf("Unexpected error: %v", err) 442 } 443 if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil { 444 t.Fatalf("Error, it should fail adding a port filter with a wrong protocol") 445 } 446 447 // Proto filter 448 filterV4 := &ConntrackFilter{} 449 err = filterV4.AddProtocol(6) 450 if err != nil { 451 t.Fatalf("Unexpected error: %v", err) 452 } 453 454 filterV6 := &ConntrackFilter{} 455 err = filterV6.AddProtocol(132) 456 if err != nil { 457 t.Fatalf("Unexpected error: %v", err) 458 } 459 460 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 461 if v4Match != 1 || v6Match != 1 { 462 t.Fatalf("Error, there should be only 1 match for TCP:%d, UDP:%d", v4Match, v6Match) 463 } 464 465 // SrcIP filter 466 filterV4 = &ConntrackFilter{} 467 err = filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")) 468 if err != nil { 469 t.Fatalf("Unexpected error: %v", err) 470 } 471 472 filterV6 = &ConntrackFilter{} 473 err = filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee")) 474 if err != nil { 475 t.Fatalf("Unexpected error: %v", err) 476 } 477 478 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 479 if v4Match != 1 || v6Match != 1 { 480 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 481 } 482 483 // DstIp filter 484 filterV4 = &ConntrackFilter{} 485 err = filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1")) 486 if err != nil { 487 t.Fatalf("Unexpected error: %v", err) 488 } 489 490 filterV6 = &ConntrackFilter{} 491 err = filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) 492 if err != nil { 493 t.Fatalf("Unexpected error: %v", err) 494 } 495 496 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 497 if v4Match != 1 || v6Match != 1 { 498 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 499 } 500 501 // SrcIP for NAT 502 filterV4 = &ConntrackFilter{} 503 err = filterV4.AddIP(ConntrackReplySrcIP, net.ParseIP("20.0.0.1")) 504 if err != nil { 505 t.Fatalf("Unexpected error: %v", err) 506 } 507 508 filterV6 = &ConntrackFilter{} 509 err = filterV6.AddIP(ConntrackReplySrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) 510 if err != nil { 511 t.Fatalf("Unexpected error: %v", err) 512 } 513 514 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 515 if v4Match != 1 || v6Match != 1 { 516 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 517 } 518 519 // DstIP for NAT 520 filterV4 = &ConntrackFilter{} 521 err = filterV4.AddIP(ConntrackReplyDstIP, net.ParseIP("192.168.1.1")) 522 if err != nil { 523 t.Fatalf("Unexpected error: %v", err) 524 } 525 526 filterV6 = &ConntrackFilter{} 527 err = filterV6.AddIP(ConntrackReplyDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) 528 if err != nil { 529 t.Fatalf("Unexpected error: %v", err) 530 } 531 532 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 533 if v4Match != 2 || v6Match != 0 { 534 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) 535 } 536 537 // AnyIp for Nat 538 filterV4 = &ConntrackFilter{} 539 err = filterV4.AddIP(ConntrackReplyAnyIP, net.ParseIP("192.168.1.1")) 540 if err != nil { 541 t.Fatalf("Unexpected error: %v", err) 542 } 543 544 filterV6 = &ConntrackFilter{} 545 err = filterV6.AddIP(ConntrackReplyAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee")) 546 if err != nil { 547 t.Fatalf("Unexpected error: %v", err) 548 } 549 550 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 551 if v4Match != 2 || v6Match != 1 { 552 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) 553 } 554 555 // SrcIPNet filter 556 filterV4 = &ConntrackFilter{} 557 ipNet, err := ParseIPNet("10.0.0.0/12") 558 if err != nil { 559 t.Fatalf("Unexpected error: %v", err) 560 } 561 err = filterV4.AddIPNet(ConntrackOrigSrcIP, ipNet) 562 if err != nil { 563 t.Fatalf("Unexpected error: %v", err) 564 } 565 566 filterV6 = &ConntrackFilter{} 567 ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64") 568 if err != nil { 569 t.Fatalf("Unexpected error: %v", err) 570 } 571 err = filterV6.AddIPNet(ConntrackOrigSrcIP, ipNet) 572 if err != nil { 573 t.Fatalf("Unexpected error: %v", err) 574 } 575 576 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 577 if v4Match != 2 || v6Match != 1 { 578 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 579 } 580 581 // DstIpNet filter 582 filterV4 = &ConntrackFilter{} 583 ipNet, err = ParseIPNet("20.0.0.0/12") 584 if err != nil { 585 t.Fatalf("Unexpected error: %v", err) 586 } 587 err = filterV4.AddIPNet(ConntrackOrigDstIP, ipNet) 588 if err != nil { 589 t.Fatalf("Unexpected error: %v", err) 590 } 591 592 filterV6 = &ConntrackFilter{} 593 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64") 594 if err != nil { 595 t.Fatalf("Unexpected error: %v", err) 596 } 597 err = filterV6.AddIPNet(ConntrackOrigDstIP, ipNet) 598 if err != nil { 599 t.Fatalf("Unexpected error: %v", err) 600 } 601 602 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 603 if v4Match != 2 || v6Match != 1 { 604 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 605 } 606 607 // SrcIPNet for NAT 608 filterV4 = &ConntrackFilter{} 609 ipNet, err = ParseIPNet("20.0.0.0/12") 610 if err != nil { 611 t.Fatalf("Unexpected error: %v", err) 612 } 613 err = filterV4.AddIPNet(ConntrackReplySrcIP, ipNet) 614 if err != nil { 615 t.Fatalf("Unexpected error: %v", err) 616 } 617 618 filterV6 = &ConntrackFilter{} 619 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64") 620 if err != nil { 621 t.Fatalf("Unexpected error: %v", err) 622 } 623 err = filterV6.AddIPNet(ConntrackReplySrcIP, ipNet) 624 if err != nil { 625 t.Fatalf("Unexpected error: %v", err) 626 } 627 628 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 629 if v4Match != 2 || v6Match != 1 { 630 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 631 } 632 633 // DstIPNet for NAT 634 filterV4 = &ConntrackFilter{} 635 ipNet, err = ParseIPNet("192.168.0.0/12") 636 if err != nil { 637 t.Fatalf("Unexpected error: %v", err) 638 } 639 err = filterV4.AddIPNet(ConntrackReplyDstIP, ipNet) 640 if err != nil { 641 t.Fatalf("Unexpected error: %v", err) 642 } 643 644 filterV6 = &ConntrackFilter{} 645 ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64") 646 if err != nil { 647 t.Fatalf("Unexpected error: %v", err) 648 } 649 err = filterV6.AddIPNet(ConntrackReplyDstIP, ipNet) 650 if err != nil { 651 t.Fatalf("Unexpected error: %v", err) 652 } 653 654 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 655 if v4Match != 2 || v6Match != 0 { 656 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) 657 } 658 659 // AnyIpNet for Nat 660 filterV4 = &ConntrackFilter{} 661 ipNet, err = ParseIPNet("192.168.0.0/12") 662 if err != nil { 663 t.Fatalf("Unexpected error: %v", err) 664 } 665 err = filterV4.AddIPNet(ConntrackReplyAnyIP, ipNet) 666 if err != nil { 667 t.Fatalf("Unexpected error: %v", err) 668 } 669 670 filterV6 = &ConntrackFilter{} 671 ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64") 672 if err != nil { 673 t.Fatalf("Unexpected error: %v", err) 674 } 675 err = filterV6.AddIPNet(ConntrackReplyAnyIP, ipNet) 676 if err != nil { 677 t.Fatalf("Unexpected error: %v", err) 678 } 679 680 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 681 if v4Match != 2 || v6Match != 1 { 682 t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) 683 } 684 // SrcPort filter 685 filterV4 = &ConntrackFilter{} 686 err = filterV4.AddProtocol(6) 687 if err != nil { 688 t.Fatalf("Unexpected error: %v", err) 689 } 690 err = filterV4.AddPort(ConntrackOrigSrcPort, 5000) 691 if err != nil { 692 t.Fatalf("Unexpected error: %v", err) 693 } 694 695 filterV6 = &ConntrackFilter{} 696 err = filterV6.AddProtocol(132) 697 if err != nil { 698 t.Fatalf("Unexpected error: %v", err) 699 } 700 err = filterV6.AddPort(ConntrackOrigSrcPort, 1000) 701 if err != nil { 702 t.Fatalf("Unexpected error: %v", err) 703 } 704 705 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 706 if v4Match != 1 || v6Match != 1 { 707 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 708 } 709 710 // DstPort filter 711 filterV4 = &ConntrackFilter{} 712 err = filterV4.AddProtocol(6) 713 if err != nil { 714 t.Fatalf("Unexpected error: %v", err) 715 } 716 err = filterV4.AddPort(ConntrackOrigDstPort, 6000) 717 if err != nil { 718 t.Fatalf("Unexpected error: %v", err) 719 } 720 721 filterV6 = &ConntrackFilter{} 722 err = filterV6.AddProtocol(132) 723 if err != nil { 724 t.Fatalf("Unexpected error: %v", err) 725 } 726 err = filterV6.AddPort(ConntrackOrigDstPort, 2000) 727 if err != nil { 728 t.Fatalf("Unexpected error: %v", err) 729 } 730 731 v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) 732 if v4Match != 1 || v6Match != 1 { 733 t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) 734 } 735 } 736 737 func TestParseRawData(t *testing.T) { 738 if nl.NativeEndian() == binary.BigEndian { 739 t.Skip("testdata expect little-endian test executor") 740 } 741 os.Setenv("TZ", "") // print timestamps in UTC 742 tests := []struct { 743 testname string 744 rawData []byte 745 expConntrackFlow string 746 }{ 747 { 748 testname: "UDP conntrack", 749 rawData: []byte{ 750 /* Nfgenmsg header */ 751 2, 0, 0, 0, 752 /* >> nested CTA_TUPLE_ORIG */ 753 52, 0, 1, 128, 754 /* >>>> nested CTA_TUPLE_IP */ 755 20, 0, 1, 128, 756 /* >>>>>> CTA_IP_V4_SRC */ 757 8, 0, 1, 0, 758 192, 168, 0, 10, 759 /* >>>>>> CTA_IP_V4_DST */ 760 8, 0, 2, 0, 761 192, 168, 0, 3, 762 /* >>>>>> nested proto info */ 763 28, 0, 2, 128, 764 /* >>>>>>>> CTA_PROTO_NUM */ 765 5, 0, 1, 0, 766 17, 0, 0, 0, 767 /* >>>>>>>> CTA_PROTO_SRC_PORT */ 768 6, 0, 2, 0, 769 189, 1, 0, 0, 770 /* >>>>>>>> CTA_PROTO_DST_PORT */ 771 6, 0, 3, 0, 772 0, 53, 0, 0, 773 /* >> CTA_TUPLE_REPLY */ 774 52, 0, 2, 128, 775 /* >>>> nested CTA_TUPLE_IP */ 776 20, 0, 1, 128, 777 /* >>>>>> CTA_IP_V4_SRC */ 778 8, 0, 1, 0, 779 192, 168, 0, 3, 780 /* >>>>>> CTA_IP_V4_DST */ 781 8, 0, 2, 0, 782 192, 168, 0, 10, 783 /* >>>>>> nested proto info */ 784 28, 0, 2, 128, 785 /* >>>>>>>> CTA_PROTO_NUM */ 786 5, 0, 1, 0, 787 17, 0, 0, 0, 788 /* >>>>>>>> CTA_PROTO_SRC_PORT */ 789 6, 0, 2, 0, 790 0, 53, 0, 0, 791 /* >>>>>>>> CTA_PROTO_DST_PORT */ 792 6, 0, 3, 0, 793 189, 1, 0, 0, 794 /* >> CTA_STATUS */ 795 8, 0, 3, 0, 796 0, 0, 1, 138, 797 /* >> CTA_MARK */ 798 8, 0, 8, 0, 799 0, 0, 0, 5, 800 /* >> CTA_ID */ 801 8, 0, 12, 0, 802 81, 172, 253, 151, 803 /* >> CTA_USE */ 804 8, 0, 11, 0, 805 0, 0, 0, 1, 806 /* >> CTA_TIMEOUT */ 807 8, 0, 7, 0, 808 0, 0, 0, 32, 809 /* >> nested CTA_COUNTERS_ORIG */ 810 28, 0, 9, 128, 811 /* >>>> CTA_COUNTERS_PACKETS */ 812 12, 0, 1, 0, 813 0, 0, 0, 0, 0, 0, 0, 1, 814 /* >>>> CTA_COUNTERS_BYTES */ 815 12, 0, 2, 0, 816 0, 0, 0, 0, 0, 0, 0, 55, 817 /* >> nested CTA_COUNTERS_REPLY */ 818 28, 0, 10, 128, 819 /* >>>> CTA_COUNTERS_PACKETS */ 820 12, 0, 1, 0, 821 0, 0, 0, 0, 0, 0, 0, 1, 822 /* >>>> CTA_COUNTERS_BYTES */ 823 12, 0, 2, 0, 824 0, 0, 0, 0, 0, 0, 0, 71, 825 /* >> nested CTA_TIMESTAMP */ 826 16, 0, 20, 128, 827 /* >>>> CTA_TIMESTAMP_START */ 828 12, 0, 1, 0, 829 22, 134, 80, 142, 230, 127, 74, 166}, 830 expConntrackFlow: "udp\t17 src=192.168.0.10 dst=192.168.0.3 sport=48385 dport=53 packets=1 bytes=55\t" + 831 "src=192.168.0.3 dst=192.168.0.10 sport=53 dport=48385 packets=1 bytes=71 mark=0x5 " + 832 "start=2021-06-07 13:41:30.39632247 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=32(sec)", 833 }, 834 { 835 testname: "TCP conntrack", 836 rawData: []byte{ 837 /* Nfgenmsg header */ 838 2, 0, 0, 0, 839 /* >> nested CTA_TUPLE_ORIG */ 840 52, 0, 1, 128, 841 /* >>>> nested CTA_TUPLE_IP */ 842 20, 0, 1, 128, 843 /* >>>>>> CTA_IP_V4_SRC */ 844 8, 0, 1, 0, 845 192, 168, 0, 10, 846 /* >>>>>> CTA_IP_V4_DST */ 847 8, 0, 2, 0, 848 192, 168, 77, 73, 849 /* >>>>>> nested proto info */ 850 28, 0, 2, 128, 851 /* >>>>>>>> CTA_PROTO_NUM */ 852 5, 0, 1, 0, 853 6, 0, 0, 0, 854 /* >>>>>>>> CTA_PROTO_SRC_PORT */ 855 6, 0, 2, 0, 856 166, 129, 0, 0, 857 /* >>>>>>>> CTA_PROTO_DST_PORT */ 858 6, 0, 3, 0, 859 13, 5, 0, 0, 860 /* >> CTA_TUPLE_REPLY */ 861 52, 0, 2, 128, 862 /* >>>> nested CTA_TUPLE_IP */ 863 20, 0, 1, 128, 864 /* >>>>>> CTA_IP_V4_SRC */ 865 8, 0, 1, 0, 866 192, 168, 77, 73, 867 /* >>>>>> CTA_IP_V4_DST */ 868 8, 0, 2, 0, 869 192, 168, 0, 10, 870 /* >>>>>> nested proto info */ 871 28, 0, 2, 128, 872 /* >>>>>>>> CTA_PROTO_NUM */ 873 5, 0, 1, 0, 874 6, 0, 0, 0, 875 /* >>>>>>>> CTA_PROTO_SRC_PORT */ 876 6, 0, 2, 0, 877 13, 5, 0, 0, 878 /* >>>>>>>> CTA_PROTO_DST_PORT */ 879 6, 0, 3, 0, 880 166, 129, 0, 0, 881 /* >> CTA_STATUS */ 882 8, 0, 3, 0, 883 0, 0, 1, 142, 884 /* >> CTA_MARK */ 885 8, 0, 8, 0, 886 0, 0, 0, 5, 887 /* >> CTA_ID */ 888 8, 0, 12, 0, 889 177, 65, 179, 133, 890 /* >> CTA_USE */ 891 8, 0, 11, 0, 892 0, 0, 0, 1, 893 /* >> CTA_TIMEOUT */ 894 8, 0, 7, 0, 895 0, 0, 0, 152, 896 /* >> CTA_PROTOINFO */ 897 48, 0, 4, 128, 898 44, 0, 1, 128, 899 5, 0, 1, 0, 8, 0, 0, 0, 900 5, 0, 2, 0, 0, 0, 0, 0, 901 5, 0, 3, 0, 0, 0, 0, 0, 902 6, 0, 4, 0, 39, 0, 0, 0, 903 6, 0, 5, 0, 32, 0, 0, 0, 904 /* >> nested CTA_COUNTERS_ORIG */ 905 28, 0, 9, 128, 906 /* >>>> CTA_COUNTERS_PACKETS */ 907 12, 0, 1, 0, 908 0, 0, 0, 0, 0, 0, 0, 11, 909 /* >>>> CTA_COUNTERS_BYTES */ 910 12, 0, 2, 0, 911 0, 0, 0, 0, 0, 0, 7, 122, 912 /* >> nested CTA_COUNTERS_REPLY */ 913 28, 0, 10, 128, 914 /* >>>> CTA_COUNTERS_PACKETS */ 915 12, 0, 1, 0, 916 0, 0, 0, 0, 0, 0, 0, 10, 917 /* >>>> CTA_COUNTERS_BYTES */ 918 12, 0, 2, 0, 919 0, 0, 0, 0, 0, 0, 7, 66, 920 /* >> nested CTA_TIMESTAMP */ 921 16, 0, 20, 128, 922 /* >>>> CTA_TIMESTAMP_START */ 923 12, 0, 1, 0, 924 22, 134, 80, 175, 134, 10, 182, 221}, 925 expConntrackFlow: "tcp\t6 src=192.168.0.10 dst=192.168.77.73 sport=42625 dport=3333 packets=11 bytes=1914\t" + 926 "src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 " + 927 "start=2021-06-07 13:43:50.511990493 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=152(sec)", 928 }, 929 } 930 931 for _, test := range tests { 932 t.Run(test.testname, func(t *testing.T) { 933 conntrackFlow := parseRawData(test.rawData) 934 if conntrackFlow.String() != test.expConntrackFlow { 935 t.Errorf("expected conntrack flow:\n\t%q\ngot conntrack flow:\n\t%q", 936 test.expConntrackFlow, conntrackFlow) 937 } 938 }) 939 } 940 }