github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/iptables/filter_input.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package iptables 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net" 22 "time" 23 ) 24 25 const ( 26 dropPort = 2401 27 acceptPort = 2402 28 sendloopDuration = 2 * time.Second 29 chainName = "foochain" 30 ) 31 32 func init() { 33 RegisterTestCase(&FilterInputDropAll{}) 34 RegisterTestCase(&FilterInputDropDifferentUDPPort{}) 35 RegisterTestCase(&FilterInputDropOnlyUDP{}) 36 RegisterTestCase(&FilterInputDropTCPDestPort{}) 37 RegisterTestCase(&FilterInputDropTCPSrcPort{}) 38 RegisterTestCase(&FilterInputDropUDPPort{}) 39 RegisterTestCase(&FilterInputDropUDP{}) 40 RegisterTestCase(&FilterInputCreateUserChain{}) 41 RegisterTestCase(&FilterInputDefaultPolicyAccept{}) 42 RegisterTestCase(&FilterInputDefaultPolicyDrop{}) 43 RegisterTestCase(&FilterInputReturnUnderflow{}) 44 RegisterTestCase(&FilterInputSerializeJump{}) 45 RegisterTestCase(&FilterInputJumpBasic{}) 46 RegisterTestCase(&FilterInputJumpReturn{}) 47 RegisterTestCase(&FilterInputJumpReturnDrop{}) 48 RegisterTestCase(&FilterInputJumpBuiltin{}) 49 RegisterTestCase(&FilterInputJumpTwice{}) 50 RegisterTestCase(&FilterInputDestination{}) 51 RegisterTestCase(&FilterInputInvertDestination{}) 52 RegisterTestCase(&FilterInputSource{}) 53 RegisterTestCase(&FilterInputInvertSource{}) 54 RegisterTestCase(&FilterInputInterfaceAccept{}) 55 RegisterTestCase(&FilterInputInterfaceDrop{}) 56 RegisterTestCase(&FilterInputInterface{}) 57 RegisterTestCase(&FilterInputInterfaceBeginsWith{}) 58 RegisterTestCase(&FilterInputInterfaceInvertDrop{}) 59 RegisterTestCase(&FilterInputInterfaceInvertAccept{}) 60 } 61 62 // FilterInputDropUDP tests that we can drop UDP traffic. 63 type FilterInputDropUDP struct{ containerCase } 64 65 var _ TestCase = (*FilterInputDropUDP)(nil) 66 67 // Name implements TestCase.Name. 68 func (*FilterInputDropUDP) Name() string { 69 return "FilterInputDropUDP" 70 } 71 72 // ContainerAction implements TestCase.ContainerAction. 73 func (*FilterInputDropUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 74 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil { 75 return err 76 } 77 78 // Listen for UDP packets on dropPort. 79 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 80 defer cancel() 81 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 82 return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) 83 } else if !errors.Is(err, context.DeadlineExceeded) { 84 return fmt.Errorf("error reading: %v", err) 85 } 86 87 // At this point we know that reading timed out and never received a 88 // packet. 89 return nil 90 } 91 92 // LocalAction implements TestCase.LocalAction. 93 func (*FilterInputDropUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 94 return sendUDPLoop(ctx, ip, dropPort, ipv6) 95 } 96 97 // FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic. 98 type FilterInputDropOnlyUDP struct{ baseCase } 99 100 var _ TestCase = (*FilterInputDropOnlyUDP)(nil) 101 102 // Name implements TestCase.Name. 103 func (*FilterInputDropOnlyUDP) Name() string { 104 return "FilterInputDropOnlyUDP" 105 } 106 107 // ContainerAction implements TestCase.ContainerAction. 108 func (*FilterInputDropOnlyUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 109 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil { 110 return err 111 } 112 113 // Listen for a TCP connection, which should be allowed. 114 if err := listenTCP(ctx, acceptPort, ipv6); err != nil { 115 return fmt.Errorf("failed to establish a connection %v", err) 116 } 117 118 return nil 119 } 120 121 // LocalAction implements TestCase.LocalAction. 122 func (*FilterInputDropOnlyUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 123 // Try to establish a TCP connection with the container, which should 124 // succeed. 125 return connectTCP(ctx, ip, acceptPort, ipv6) 126 } 127 128 // FilterInputDropUDPPort tests that we can drop UDP traffic by port. 129 type FilterInputDropUDPPort struct{ containerCase } 130 131 var _ TestCase = (*FilterInputDropUDPPort)(nil) 132 133 // Name implements TestCase.Name. 134 func (*FilterInputDropUDPPort) Name() string { 135 return "FilterInputDropUDPPort" 136 } 137 138 // ContainerAction implements TestCase.ContainerAction. 139 func (*FilterInputDropUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 140 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil { 141 return err 142 } 143 144 // Listen for UDP packets on dropPort. 145 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 146 defer cancel() 147 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 148 return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) 149 } else if !errors.Is(err, context.DeadlineExceeded) { 150 return fmt.Errorf("error reading: %v", err) 151 } 152 153 // At this point we know that reading timed out and never received a 154 // packet. 155 return nil 156 } 157 158 // LocalAction implements TestCase.LocalAction. 159 func (*FilterInputDropUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 160 return sendUDPLoop(ctx, ip, dropPort, ipv6) 161 } 162 163 // FilterInputDropDifferentUDPPort tests that dropping traffic for a single UDP port 164 // doesn't drop packets on other ports. 165 type FilterInputDropDifferentUDPPort struct{ containerCase } 166 167 var _ TestCase = (*FilterInputDropDifferentUDPPort)(nil) 168 169 // Name implements TestCase.Name. 170 func (*FilterInputDropDifferentUDPPort) Name() string { 171 return "FilterInputDropDifferentUDPPort" 172 } 173 174 // ContainerAction implements TestCase.ContainerAction. 175 func (*FilterInputDropDifferentUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 176 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil { 177 return err 178 } 179 180 // Listen for UDP packets on another port. 181 if err := listenUDP(ctx, acceptPort, ipv6); err != nil { 182 return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", acceptPort, err) 183 } 184 185 return nil 186 } 187 188 // LocalAction implements TestCase.LocalAction. 189 func (*FilterInputDropDifferentUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 190 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 191 } 192 193 // FilterInputDropTCPDestPort tests that connections are not accepted on specified source ports. 194 type FilterInputDropTCPDestPort struct{ baseCase } 195 196 var _ TestCase = (*FilterInputDropTCPDestPort)(nil) 197 198 // Name implements TestCase.Name. 199 func (*FilterInputDropTCPDestPort) Name() string { 200 return "FilterInputDropTCPDestPort" 201 } 202 203 // ContainerAction implements TestCase.ContainerAction. 204 func (*FilterInputDropTCPDestPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 205 if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil { 206 return err 207 } 208 209 // Listen for TCP packets on drop port. 210 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 211 defer cancel() 212 if err := listenTCP(timedCtx, dropPort, ipv6); err == nil { 213 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) 214 } else if !errors.Is(err, context.DeadlineExceeded) { 215 return fmt.Errorf("error reading: %v", err) 216 } 217 218 return nil 219 } 220 221 // LocalAction implements TestCase.LocalAction. 222 func (*FilterInputDropTCPDestPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 223 // Ensure we cannot connect to the container. 224 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 225 defer cancel() 226 if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil { 227 return fmt.Errorf("expected not to connect, but was able to connect on port %d", dropPort) 228 } 229 return nil 230 } 231 232 // FilterInputDropTCPSrcPort tests that connections are not accepted on specified source ports. 233 type FilterInputDropTCPSrcPort struct{ baseCase } 234 235 var _ TestCase = (*FilterInputDropTCPSrcPort)(nil) 236 237 // Name implements TestCase.Name. 238 func (*FilterInputDropTCPSrcPort) Name() string { 239 return "FilterInputDropTCPSrcPort" 240 } 241 242 // ContainerAction implements TestCase.ContainerAction. 243 func (*FilterInputDropTCPSrcPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 244 // Drop anything from an ephemeral port. 245 if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", "1024:65535", "-j", "DROP"); err != nil { 246 return err 247 } 248 249 // Listen for TCP packets on accept port. 250 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 251 defer cancel() 252 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 253 return fmt.Errorf("connection destined to port %d should not be accepted, but was", dropPort) 254 } else if !errors.Is(err, context.DeadlineExceeded) { 255 return fmt.Errorf("error reading: %v", err) 256 } 257 258 return nil 259 } 260 261 // LocalAction implements TestCase.LocalAction. 262 func (*FilterInputDropTCPSrcPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 263 // Ensure we cannot connect to the container. 264 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 265 defer cancel() 266 if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil { 267 return fmt.Errorf("expected not to connect, but was able to connect on port %d", acceptPort) 268 } 269 return nil 270 } 271 272 // FilterInputDropAll tests that we can drop all traffic to the INPUT chain. 273 type FilterInputDropAll struct{ containerCase } 274 275 var _ TestCase = (*FilterInputDropAll)(nil) 276 277 // Name implements TestCase.Name. 278 func (*FilterInputDropAll) Name() string { 279 return "FilterInputDropAll" 280 } 281 282 // ContainerAction implements TestCase.ContainerAction. 283 func (*FilterInputDropAll) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 284 if err := filterTable(ipv6, "-A", "INPUT", "-j", "DROP"); err != nil { 285 return err 286 } 287 288 // Listen for all packets on dropPort. 289 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 290 defer cancel() 291 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 292 return fmt.Errorf("packets should have been dropped, but got a packet") 293 } else if !errors.Is(err, context.DeadlineExceeded) { 294 return fmt.Errorf("error reading: %v", err) 295 } 296 297 // At this point we know that reading timed out and never received a 298 // packet. 299 return nil 300 } 301 302 // LocalAction implements TestCase.LocalAction. 303 func (*FilterInputDropAll) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 304 return sendUDPLoop(ctx, ip, dropPort, ipv6) 305 } 306 307 // FilterInputMultiUDPRules verifies that multiple UDP rules are applied 308 // correctly. This has the added benefit of testing whether we're serializing 309 // rules correctly -- if we do it incorrectly, the iptables tool will 310 // misunderstand and save the wrong tables. 311 type FilterInputMultiUDPRules struct{ baseCase } 312 313 var _ TestCase = (*FilterInputMultiUDPRules)(nil) 314 315 // Name implements TestCase.Name. 316 func (*FilterInputMultiUDPRules) Name() string { 317 return "FilterInputMultiUDPRules" 318 } 319 320 // ContainerAction implements TestCase.ContainerAction. 321 func (*FilterInputMultiUDPRules) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 322 rules := [][]string{ 323 {"-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"}, 324 {"-A", "INPUT", "-p", "udp", "-m", "udp", "--destination-port", fmt.Sprintf("%d", acceptPort), "-j", "ACCEPT"}, 325 {"-L"}, 326 } 327 return filterTableRules(ipv6, rules) 328 } 329 330 // LocalAction implements TestCase.LocalAction. 331 func (*FilterInputMultiUDPRules) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 332 // No-op. 333 return nil 334 } 335 336 // FilterInputRequireProtocolUDP checks that "-m udp" requires "-p udp" to be 337 // specified. 338 type FilterInputRequireProtocolUDP struct{ baseCase } 339 340 var _ TestCase = (*FilterInputRequireProtocolUDP)(nil) 341 342 // Name implements TestCase.Name. 343 func (*FilterInputRequireProtocolUDP) Name() string { 344 return "FilterInputRequireProtocolUDP" 345 } 346 347 // ContainerAction implements TestCase.ContainerAction. 348 func (*FilterInputRequireProtocolUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 349 if err := filterTable(ipv6, "-A", "INPUT", "-m", "udp", "--destination-port", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err == nil { 350 return errors.New("expected iptables to fail with out \"-p udp\", but succeeded") 351 } 352 return nil 353 } 354 355 // LocalAction implements TestCase.LocalAction. 356 func (*FilterInputRequireProtocolUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 357 // No-op. 358 return nil 359 } 360 361 // FilterInputCreateUserChain tests chain creation. 362 type FilterInputCreateUserChain struct{ baseCase } 363 364 var _ TestCase = (*FilterInputCreateUserChain)(nil) 365 366 // Name implements TestCase.Name. 367 func (*FilterInputCreateUserChain) Name() string { 368 return "FilterInputCreateUserChain" 369 } 370 371 // ContainerAction implements TestCase.ContainerAction. 372 func (*FilterInputCreateUserChain) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 373 rules := [][]string{ 374 // Create a chain. 375 {"-N", chainName}, 376 // Add a simple rule to the chain. 377 {"-A", chainName, "-j", "DROP"}, 378 } 379 return filterTableRules(ipv6, rules) 380 } 381 382 // LocalAction implements TestCase.LocalAction. 383 func (*FilterInputCreateUserChain) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 384 // No-op. 385 return nil 386 } 387 388 // FilterInputDefaultPolicyAccept tests the default ACCEPT policy. 389 type FilterInputDefaultPolicyAccept struct{ containerCase } 390 391 var _ TestCase = (*FilterInputDefaultPolicyAccept)(nil) 392 393 // Name implements TestCase.Name. 394 func (*FilterInputDefaultPolicyAccept) Name() string { 395 return "FilterInputDefaultPolicyAccept" 396 } 397 398 // ContainerAction implements TestCase.ContainerAction. 399 func (*FilterInputDefaultPolicyAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 400 // Set the default policy to accept, then receive a packet. 401 if err := filterTable(ipv6, "-P", "INPUT", "ACCEPT"); err != nil { 402 return err 403 } 404 return listenUDP(ctx, acceptPort, ipv6) 405 } 406 407 // LocalAction implements TestCase.LocalAction. 408 func (*FilterInputDefaultPolicyAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 409 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 410 } 411 412 // FilterInputDefaultPolicyDrop tests the default DROP policy. 413 type FilterInputDefaultPolicyDrop struct{ containerCase } 414 415 var _ TestCase = (*FilterInputDefaultPolicyDrop)(nil) 416 417 // Name implements TestCase.Name. 418 func (*FilterInputDefaultPolicyDrop) Name() string { 419 return "FilterInputDefaultPolicyDrop" 420 } 421 422 // ContainerAction implements TestCase.ContainerAction. 423 func (*FilterInputDefaultPolicyDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 424 if err := filterTable(ipv6, "-P", "INPUT", "DROP"); err != nil { 425 return err 426 } 427 428 // Listen for UDP packets on dropPort. 429 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 430 defer cancel() 431 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 432 return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) 433 } else if !errors.Is(err, context.DeadlineExceeded) { 434 return fmt.Errorf("error reading: %v", err) 435 } 436 437 // At this point we know that reading timed out and never received a 438 // packet. 439 return nil 440 } 441 442 // LocalAction implements TestCase.LocalAction. 443 func (*FilterInputDefaultPolicyDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 444 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 445 } 446 447 // FilterInputReturnUnderflow tests that -j RETURN in a built-in chain causes 448 // the underflow rule (i.e. default policy) to be executed. 449 type FilterInputReturnUnderflow struct{ containerCase } 450 451 var _ TestCase = (*FilterInputReturnUnderflow)(nil) 452 453 // Name implements TestCase.Name. 454 func (*FilterInputReturnUnderflow) Name() string { 455 return "FilterInputReturnUnderflow" 456 } 457 458 // ContainerAction implements TestCase.ContainerAction. 459 func (*FilterInputReturnUnderflow) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 460 // Add a RETURN rule followed by an unconditional accept, and set the 461 // default policy to DROP. 462 rules := [][]string{ 463 {"-A", "INPUT", "-j", "RETURN"}, 464 {"-A", "INPUT", "-j", "DROP"}, 465 {"-P", "INPUT", "ACCEPT"}, 466 } 467 if err := filterTableRules(ipv6, rules); err != nil { 468 return err 469 } 470 471 // We should receive packets, as the RETURN rule will trigger the default 472 // ACCEPT policy. 473 return listenUDP(ctx, acceptPort, ipv6) 474 } 475 476 // LocalAction implements TestCase.LocalAction. 477 func (*FilterInputReturnUnderflow) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 478 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 479 } 480 481 // FilterInputSerializeJump verifies that we can serialize jumps. 482 type FilterInputSerializeJump struct{ baseCase } 483 484 var _ TestCase = (*FilterInputSerializeJump)(nil) 485 486 // Name implements TestCase.Name. 487 func (*FilterInputSerializeJump) Name() string { 488 return "FilterInputSerializeJump" 489 } 490 491 // ContainerAction implements TestCase.ContainerAction. 492 func (*FilterInputSerializeJump) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 493 // Write a JUMP rule, the serialize it with `-L`. 494 rules := [][]string{ 495 {"-N", chainName}, 496 {"-A", "INPUT", "-j", chainName}, 497 {"-L"}, 498 } 499 return filterTableRules(ipv6, rules) 500 } 501 502 // LocalAction implements TestCase.LocalAction. 503 func (*FilterInputSerializeJump) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 504 // No-op. 505 return nil 506 } 507 508 // FilterInputJumpBasic jumps to a chain and executes a rule there. 509 type FilterInputJumpBasic struct{ containerCase } 510 511 var _ TestCase = (*FilterInputJumpBasic)(nil) 512 513 // Name implements TestCase.Name. 514 func (*FilterInputJumpBasic) Name() string { 515 return "FilterInputJumpBasic" 516 } 517 518 // ContainerAction implements TestCase.ContainerAction. 519 func (*FilterInputJumpBasic) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 520 rules := [][]string{ 521 {"-P", "INPUT", "DROP"}, 522 {"-N", chainName}, 523 {"-A", "INPUT", "-j", chainName}, 524 {"-A", chainName, "-j", "ACCEPT"}, 525 } 526 if err := filterTableRules(ipv6, rules); err != nil { 527 return err 528 } 529 530 // Listen for UDP packets on acceptPort. 531 return listenUDP(ctx, acceptPort, ipv6) 532 } 533 534 // LocalAction implements TestCase.LocalAction. 535 func (*FilterInputJumpBasic) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 536 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 537 } 538 539 // FilterInputJumpReturn jumps, returns, and executes a rule. 540 type FilterInputJumpReturn struct{ containerCase } 541 542 var _ TestCase = (*FilterInputJumpReturn)(nil) 543 544 // Name implements TestCase.Name. 545 func (*FilterInputJumpReturn) Name() string { 546 return "FilterInputJumpReturn" 547 } 548 549 // ContainerAction implements TestCase.ContainerAction. 550 func (*FilterInputJumpReturn) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 551 rules := [][]string{ 552 {"-N", chainName}, 553 {"-P", "INPUT", "ACCEPT"}, 554 {"-A", "INPUT", "-j", chainName}, 555 {"-A", chainName, "-j", "RETURN"}, 556 {"-A", chainName, "-j", "DROP"}, 557 } 558 if err := filterTableRules(ipv6, rules); err != nil { 559 return err 560 } 561 562 // Listen for UDP packets on acceptPort. 563 return listenUDP(ctx, acceptPort, ipv6) 564 } 565 566 // LocalAction implements TestCase.LocalAction. 567 func (*FilterInputJumpReturn) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 568 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 569 } 570 571 // FilterInputJumpReturnDrop jumps to a chain, returns, and DROPs packets. 572 type FilterInputJumpReturnDrop struct{ containerCase } 573 574 var _ TestCase = (*FilterInputJumpReturnDrop)(nil) 575 576 // Name implements TestCase.Name. 577 func (*FilterInputJumpReturnDrop) Name() string { 578 return "FilterInputJumpReturnDrop" 579 } 580 581 // ContainerAction implements TestCase.ContainerAction. 582 func (*FilterInputJumpReturnDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 583 rules := [][]string{ 584 {"-N", chainName}, 585 {"-A", "INPUT", "-j", chainName}, 586 {"-A", "INPUT", "-j", "DROP"}, 587 {"-A", chainName, "-j", "RETURN"}, 588 } 589 if err := filterTableRules(ipv6, rules); err != nil { 590 return err 591 } 592 593 // Listen for UDP packets on dropPort. 594 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 595 defer cancel() 596 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 597 return fmt.Errorf("packets on port %d should have been dropped, but got a packet", dropPort) 598 } else if !errors.Is(err, context.DeadlineExceeded) { 599 return fmt.Errorf("error reading: %v", err) 600 } 601 602 // At this point we know that reading timed out and never received a 603 // packet. 604 return nil 605 } 606 607 // LocalAction implements TestCase.LocalAction. 608 func (*FilterInputJumpReturnDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 609 return sendUDPLoop(ctx, ip, dropPort, ipv6) 610 } 611 612 // FilterInputJumpBuiltin verifies that jumping to a top-levl chain is illegal. 613 type FilterInputJumpBuiltin struct{ baseCase } 614 615 var _ TestCase = (*FilterInputJumpBuiltin)(nil) 616 617 // Name implements TestCase.Name. 618 func (*FilterInputJumpBuiltin) Name() string { 619 return "FilterInputJumpBuiltin" 620 } 621 622 // ContainerAction implements TestCase.ContainerAction. 623 func (*FilterInputJumpBuiltin) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 624 if err := filterTable(ipv6, "-A", "INPUT", "-j", "OUTPUT"); err == nil { 625 return fmt.Errorf("iptables should be unable to jump to a built-in chain") 626 } 627 return nil 628 } 629 630 // LocalAction implements TestCase.LocalAction. 631 func (*FilterInputJumpBuiltin) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 632 // No-op. 633 return nil 634 } 635 636 // FilterInputJumpTwice jumps twice, then returns twice and executes a rule. 637 type FilterInputJumpTwice struct{ containerCase } 638 639 var _ TestCase = (*FilterInputJumpTwice)(nil) 640 641 // Name implements TestCase.Name. 642 func (*FilterInputJumpTwice) Name() string { 643 return "FilterInputJumpTwice" 644 } 645 646 // ContainerAction implements TestCase.ContainerAction. 647 func (*FilterInputJumpTwice) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 648 const chainName2 = chainName + "2" 649 rules := [][]string{ 650 {"-P", "INPUT", "DROP"}, 651 {"-N", chainName}, 652 {"-N", chainName2}, 653 {"-A", "INPUT", "-j", chainName}, 654 {"-A", chainName, "-j", chainName2}, 655 {"-A", "INPUT", "-j", "ACCEPT"}, 656 } 657 if err := filterTableRules(ipv6, rules); err != nil { 658 return err 659 } 660 661 // UDP packets should jump and return twice, eventually hitting the 662 // ACCEPT rule. 663 return listenUDP(ctx, acceptPort, ipv6) 664 } 665 666 // LocalAction implements TestCase.LocalAction. 667 func (*FilterInputJumpTwice) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 668 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 669 } 670 671 // FilterInputDestination verifies that we can filter packets via `-d 672 // <ipaddr>`. 673 type FilterInputDestination struct{ containerCase } 674 675 var _ TestCase = (*FilterInputDestination)(nil) 676 677 // Name implements TestCase.Name. 678 func (*FilterInputDestination) Name() string { 679 return "FilterInputDestination" 680 } 681 682 // ContainerAction implements TestCase.ContainerAction. 683 func (*FilterInputDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 684 addrs, err := localAddrs(ipv6) 685 if err != nil { 686 return err 687 } 688 689 // Make INPUT's default action DROP, then ACCEPT all packets bound for 690 // this machine. 691 rules := [][]string{{"-P", "INPUT", "DROP"}} 692 for _, addr := range addrs { 693 rules = append(rules, []string{"-A", "INPUT", "-d", addr, "-j", "ACCEPT"}) 694 } 695 if err := filterTableRules(ipv6, rules); err != nil { 696 return err 697 } 698 699 return listenUDP(ctx, acceptPort, ipv6) 700 } 701 702 // LocalAction implements TestCase.LocalAction. 703 func (*FilterInputDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 704 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 705 } 706 707 // FilterInputInvertDestination verifies that we can filter packets via `! -d 708 // <ipaddr>`. 709 type FilterInputInvertDestination struct{ containerCase } 710 711 var _ TestCase = (*FilterInputInvertDestination)(nil) 712 713 // Name implements TestCase.Name. 714 func (*FilterInputInvertDestination) Name() string { 715 return "FilterInputInvertDestination" 716 } 717 718 // ContainerAction implements TestCase.ContainerAction. 719 func (*FilterInputInvertDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 720 // Make INPUT's default action DROP, then ACCEPT all packets not bound 721 // for 127.0.0.1. 722 rules := [][]string{ 723 {"-P", "INPUT", "DROP"}, 724 {"-A", "INPUT", "!", "-d", localIP(ipv6), "-j", "ACCEPT"}, 725 } 726 if err := filterTableRules(ipv6, rules); err != nil { 727 return err 728 } 729 730 return listenUDP(ctx, acceptPort, ipv6) 731 } 732 733 // LocalAction implements TestCase.LocalAction. 734 func (*FilterInputInvertDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 735 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 736 } 737 738 // FilterInputSource verifies that we can filter packets via `-s 739 // <ipaddr>`. 740 type FilterInputSource struct{ containerCase } 741 742 var _ TestCase = (*FilterInputSource)(nil) 743 744 // Name implements TestCase.Name. 745 func (*FilterInputSource) Name() string { 746 return "FilterInputSource" 747 } 748 749 // ContainerAction implements TestCase.ContainerAction. 750 func (*FilterInputSource) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 751 // Make INPUT's default action DROP, then ACCEPT all packets from this 752 // machine. 753 rules := [][]string{ 754 {"-P", "INPUT", "DROP"}, 755 {"-A", "INPUT", "-s", fmt.Sprintf("%v", ip), "-j", "ACCEPT"}, 756 } 757 if err := filterTableRules(ipv6, rules); err != nil { 758 return err 759 } 760 761 return listenUDP(ctx, acceptPort, ipv6) 762 } 763 764 // LocalAction implements TestCase.LocalAction. 765 func (*FilterInputSource) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 766 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 767 } 768 769 // FilterInputInvertSource verifies that we can filter packets via `! -s 770 // <ipaddr>`. 771 type FilterInputInvertSource struct{ containerCase } 772 773 var _ TestCase = (*FilterInputInvertSource)(nil) 774 775 // Name implements TestCase.Name. 776 func (*FilterInputInvertSource) Name() string { 777 return "FilterInputInvertSource" 778 } 779 780 // ContainerAction implements TestCase.ContainerAction. 781 func (*FilterInputInvertSource) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 782 // Make INPUT's default action DROP, then ACCEPT all packets not bound 783 // for 127.0.0.1. 784 rules := [][]string{ 785 {"-P", "INPUT", "DROP"}, 786 {"-A", "INPUT", "!", "-s", localIP(ipv6), "-j", "ACCEPT"}, 787 } 788 if err := filterTableRules(ipv6, rules); err != nil { 789 return err 790 } 791 792 return listenUDP(ctx, acceptPort, ipv6) 793 } 794 795 // LocalAction implements TestCase.LocalAction. 796 func (*FilterInputInvertSource) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 797 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 798 } 799 800 // FilterInputInterfaceAccept tests that packets are accepted from interface 801 // matching the iptables rule. 802 type FilterInputInterfaceAccept struct{ localCase } 803 804 var _ TestCase = (*FilterInputInterfaceAccept)(nil) 805 806 // Name implements TestCase.Name. 807 func (*FilterInputInterfaceAccept) Name() string { 808 return "FilterInputInterfaceAccept" 809 } 810 811 // ContainerAction implements TestCase.ContainerAction. 812 func (*FilterInputInterfaceAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 813 ifname, ok := getInterfaceName() 814 if !ok { 815 return fmt.Errorf("no interface is present, except loopback") 816 } 817 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", ifname, "-j", "ACCEPT"); err != nil { 818 return err 819 } 820 if err := listenUDP(ctx, acceptPort, ipv6); err != nil { 821 return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %w", acceptPort, err) 822 } 823 824 return nil 825 } 826 827 // LocalAction implements TestCase.LocalAction. 828 func (*FilterInputInterfaceAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 829 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 830 } 831 832 // FilterInputInterfaceDrop tests that packets are dropped from interface 833 // matching the iptables rule. 834 type FilterInputInterfaceDrop struct{ localCase } 835 836 var _ TestCase = (*FilterInputInterfaceDrop)(nil) 837 838 // Name implements TestCase.Name. 839 func (*FilterInputInterfaceDrop) Name() string { 840 return "FilterInputInterfaceDrop" 841 } 842 843 // ContainerAction implements TestCase.ContainerAction. 844 func (*FilterInputInterfaceDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 845 ifname, ok := getInterfaceName() 846 if !ok { 847 return fmt.Errorf("no interface is present, except loopback") 848 } 849 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", ifname, "-j", "DROP"); err != nil { 850 return err 851 } 852 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 853 defer cancel() 854 if err := listenUDP(timedCtx, acceptPort, ipv6); err != nil { 855 if errors.Is(err, context.DeadlineExceeded) { 856 return nil 857 } 858 return fmt.Errorf("error reading: %w", err) 859 } 860 return fmt.Errorf("packets should have been dropped, but got a packet") 861 } 862 863 // LocalAction implements TestCase.LocalAction. 864 func (*FilterInputInterfaceDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 865 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 866 } 867 868 // FilterInputInterface tests that packets are not dropped from interface which 869 // is not matching the interface name in the iptables rule. 870 type FilterInputInterface struct{ localCase } 871 872 var _ TestCase = (*FilterInputInterface)(nil) 873 874 // Name implements TestCase.Name. 875 func (*FilterInputInterface) Name() string { 876 return "FilterInputInterface" 877 } 878 879 // ContainerAction implements TestCase.ContainerAction. 880 func (*FilterInputInterface) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 881 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", "lo", "-j", "DROP"); err != nil { 882 return err 883 } 884 if err := listenUDP(ctx, acceptPort, ipv6); err != nil { 885 return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %w", acceptPort, err) 886 } 887 return nil 888 } 889 890 // LocalAction implements TestCase.LocalAction. 891 func (*FilterInputInterface) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 892 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 893 } 894 895 // FilterInputInterfaceBeginsWith tests that packets are dropped from an 896 // interface which begins with the given interface name. 897 type FilterInputInterfaceBeginsWith struct{ localCase } 898 899 var _ TestCase = (*FilterInputInterfaceBeginsWith)(nil) 900 901 // Name implements TestCase.Name. 902 func (*FilterInputInterfaceBeginsWith) Name() string { 903 return "FilterInputInterfaceBeginsWith" 904 } 905 906 // ContainerAction implements TestCase.ContainerAction. 907 func (*FilterInputInterfaceBeginsWith) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 908 if err := filterTable(ipv6, "-A", "INPUT", "-p", "udp", "-i", "e+", "-j", "DROP"); err != nil { 909 return err 910 } 911 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 912 defer cancel() 913 if err := listenUDP(timedCtx, acceptPort, ipv6); err != nil { 914 if errors.Is(err, context.DeadlineExceeded) { 915 return nil 916 } 917 return fmt.Errorf("error reading: %w", err) 918 } 919 return fmt.Errorf("packets should have been dropped, but got a packet") 920 } 921 922 // LocalAction implements TestCase.LocalAction. 923 func (*FilterInputInterfaceBeginsWith) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 924 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 925 } 926 927 // FilterInputInterfaceInvertDrop tests that we selectively drop packets from 928 // interface not matching the interface name. 929 type FilterInputInterfaceInvertDrop struct{ baseCase } 930 931 var _ TestCase = (*FilterInputInterfaceInvertDrop)(nil) 932 933 // Name implements TestCase.Name. 934 func (*FilterInputInterfaceInvertDrop) Name() string { 935 return "FilterInputInterfaceInvertDrop" 936 } 937 938 // ContainerAction implements TestCase.ContainerAction. 939 func (*FilterInputInterfaceInvertDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 940 if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "!", "-i", "lo", "-j", "DROP"); err != nil { 941 return err 942 } 943 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 944 defer cancel() 945 if err := listenTCP(timedCtx, acceptPort, ipv6); err != nil { 946 if errors.Is(err, context.DeadlineExceeded) { 947 return nil 948 } 949 return fmt.Errorf("error reading: %w", err) 950 } 951 return fmt.Errorf("connection on port %d should not be accepted, but was accepted", acceptPort) 952 } 953 954 // LocalAction implements TestCase.LocalAction. 955 func (*FilterInputInterfaceInvertDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 956 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 957 defer cancel() 958 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err != nil { 959 var operr *net.OpError 960 if errors.As(err, &operr) && operr.Timeout() { 961 return nil 962 } 963 return fmt.Errorf("error connecting: %w", err) 964 } 965 return fmt.Errorf("connection destined to port %d should not be accepted, but was accepted", acceptPort) 966 } 967 968 // FilterInputInterfaceInvertAccept tests that we can selectively accept packets 969 // not matching the specific incoming interface. 970 type FilterInputInterfaceInvertAccept struct{ baseCase } 971 972 var _ TestCase = (*FilterInputInterfaceInvertAccept)(nil) 973 974 // Name implements TestCase.Name. 975 func (*FilterInputInterfaceInvertAccept) Name() string { 976 return "FilterInputInterfaceInvertAccept" 977 } 978 979 // ContainerAction implements TestCase.ContainerAction. 980 func (*FilterInputInterfaceInvertAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 981 if err := filterTable(ipv6, "-A", "INPUT", "-p", "tcp", "!", "-i", "lo", "-j", "ACCEPT"); err != nil { 982 return err 983 } 984 return listenTCP(ctx, acceptPort, ipv6) 985 } 986 987 // LocalAction implements TestCase.LocalAction. 988 func (*FilterInputInterfaceInvertAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 989 return connectTCP(ctx, ip, acceptPort, ipv6) 990 }