github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/iptables/filter_output.go (about) 1 // Copyright 2020 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 ) 23 24 func init() { 25 RegisterTestCase(&FilterOutputDropTCPDestPort{}) 26 RegisterTestCase(&FilterOutputDropTCPSrcPort{}) 27 RegisterTestCase(&FilterOutputDestination{}) 28 RegisterTestCase(&FilterOutputInvertDestination{}) 29 RegisterTestCase(&FilterOutputAcceptTCPOwner{}) 30 RegisterTestCase(&FilterOutputDropTCPOwner{}) 31 RegisterTestCase(&FilterOutputAcceptUDPOwner{}) 32 RegisterTestCase(&FilterOutputDropUDPOwner{}) 33 RegisterTestCase(&FilterOutputOwnerFail{}) 34 RegisterTestCase(&FilterOutputAcceptGIDOwner{}) 35 RegisterTestCase(&FilterOutputDropGIDOwner{}) 36 RegisterTestCase(&FilterOutputInvertGIDOwner{}) 37 RegisterTestCase(&FilterOutputInvertUIDOwner{}) 38 RegisterTestCase(&FilterOutputInvertUIDAndGIDOwner{}) 39 RegisterTestCase(&FilterOutputInterfaceAccept{}) 40 RegisterTestCase(&FilterOutputInterfaceDrop{}) 41 RegisterTestCase(&FilterOutputInterface{}) 42 RegisterTestCase(&FilterOutputInterfaceBeginsWith{}) 43 RegisterTestCase(&FilterOutputInterfaceInvertDrop{}) 44 RegisterTestCase(&FilterOutputInterfaceInvertAccept{}) 45 } 46 47 // FilterOutputDropTCPDestPort tests that connections are not accepted on 48 // specified source ports. 49 type FilterOutputDropTCPDestPort struct{ baseCase } 50 51 var _ TestCase = (*FilterOutputDropTCPDestPort)(nil) 52 53 // Name implements TestCase.Name. 54 func (*FilterOutputDropTCPDestPort) Name() string { 55 return "FilterOutputDropTCPDestPort" 56 } 57 58 // ContainerAction implements TestCase.ContainerAction. 59 func (*FilterOutputDropTCPDestPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 60 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", "1024:65535", "-j", "DROP"); err != nil { 61 return err 62 } 63 64 // Listen for TCP packets on accept port. 65 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 66 defer cancel() 67 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 68 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort) 69 } else if !errors.Is(err, context.DeadlineExceeded) { 70 return fmt.Errorf("error reading: %v", err) 71 } 72 73 return nil 74 } 75 76 // LocalAction implements TestCase.LocalAction. 77 func (*FilterOutputDropTCPDestPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 78 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 79 defer cancel() 80 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 81 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) 82 } 83 84 return nil 85 } 86 87 // FilterOutputDropTCPSrcPort tests that connections are not accepted on 88 // specified source ports. 89 type FilterOutputDropTCPSrcPort struct{ baseCase } 90 91 var _ TestCase = (*FilterOutputDropTCPSrcPort)(nil) 92 93 // Name implements TestCase.Name. 94 func (*FilterOutputDropTCPSrcPort) Name() string { 95 return "FilterOutputDropTCPSrcPort" 96 } 97 98 // ContainerAction implements TestCase.ContainerAction. 99 func (*FilterOutputDropTCPSrcPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 100 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--sport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil { 101 return err 102 } 103 104 // Listen for TCP packets on drop port. 105 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 106 defer cancel() 107 if err := listenTCP(timedCtx, dropPort, ipv6); err == nil { 108 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort) 109 } else if !errors.Is(err, context.DeadlineExceeded) { 110 return fmt.Errorf("error reading: %v", err) 111 } 112 113 return nil 114 } 115 116 // LocalAction implements TestCase.LocalAction. 117 func (*FilterOutputDropTCPSrcPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 118 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 119 defer cancel() 120 if err := connectTCP(timedCtx, ip, dropPort, ipv6); err == nil { 121 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort) 122 } 123 124 return nil 125 } 126 127 // FilterOutputAcceptTCPOwner tests that TCP connections from uid owner are accepted. 128 type FilterOutputAcceptTCPOwner struct{ baseCase } 129 130 var _ TestCase = (*FilterOutputAcceptTCPOwner)(nil) 131 132 // Name implements TestCase.Name. 133 func (*FilterOutputAcceptTCPOwner) Name() string { 134 return "FilterOutputAcceptTCPOwner" 135 } 136 137 // ContainerAction implements TestCase.ContainerAction. 138 func (*FilterOutputAcceptTCPOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 139 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil { 140 return err 141 } 142 143 // Listen for TCP packets on accept port. 144 return listenTCP(ctx, acceptPort, ipv6) 145 } 146 147 // LocalAction implements TestCase.LocalAction. 148 func (*FilterOutputAcceptTCPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 149 return connectTCP(ctx, ip, acceptPort, ipv6) 150 } 151 152 // FilterOutputDropTCPOwner tests that TCP connections from uid owner are dropped. 153 type FilterOutputDropTCPOwner struct{ baseCase } 154 155 var _ TestCase = (*FilterOutputDropTCPOwner)(nil) 156 157 // Name implements TestCase.Name. 158 func (*FilterOutputDropTCPOwner) Name() string { 159 return "FilterOutputDropTCPOwner" 160 } 161 162 // ContainerAction implements TestCase.ContainerAction. 163 func (*FilterOutputDropTCPOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 164 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil { 165 return err 166 } 167 168 // Listen for TCP packets on accept port. 169 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 170 defer cancel() 171 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 172 return fmt.Errorf("connection on port %d should be dropped, but got accepted", acceptPort) 173 } else if !errors.Is(err, context.DeadlineExceeded) { 174 return fmt.Errorf("error reading: %v", err) 175 } 176 177 return nil 178 } 179 180 // LocalAction implements TestCase.LocalAction. 181 func (*FilterOutputDropTCPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 182 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 183 defer cancel() 184 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 185 return fmt.Errorf("connection destined to port %d should be dropped, but got accepted", acceptPort) 186 } 187 188 return nil 189 } 190 191 // FilterOutputAcceptUDPOwner tests that UDP packets from uid owner are accepted. 192 type FilterOutputAcceptUDPOwner struct{ localCase } 193 194 var _ TestCase = (*FilterOutputAcceptUDPOwner)(nil) 195 196 // Name implements TestCase.Name. 197 func (*FilterOutputAcceptUDPOwner) Name() string { 198 return "FilterOutputAcceptUDPOwner" 199 } 200 201 // ContainerAction implements TestCase.ContainerAction. 202 func (*FilterOutputAcceptUDPOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 203 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "ACCEPT"); err != nil { 204 return err 205 } 206 207 // Send UDP packets on acceptPort. 208 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 209 } 210 211 // LocalAction implements TestCase.LocalAction. 212 func (*FilterOutputAcceptUDPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 213 // Listen for UDP packets on acceptPort. 214 return listenUDP(ctx, acceptPort, ipv6) 215 } 216 217 // FilterOutputDropUDPOwner tests that UDP packets from uid owner are dropped. 218 type FilterOutputDropUDPOwner struct{ localCase } 219 220 var _ TestCase = (*FilterOutputDropUDPOwner)(nil) 221 222 // Name implements TestCase.Name. 223 func (*FilterOutputDropUDPOwner) Name() string { 224 return "FilterOutputDropUDPOwner" 225 } 226 227 // ContainerAction implements TestCase.ContainerAction. 228 func (*FilterOutputDropUDPOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 229 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "--uid-owner", "root", "-j", "DROP"); err != nil { 230 return err 231 } 232 233 // Send UDP packets on dropPort. 234 return sendUDPLoop(ctx, ip, dropPort, ipv6) 235 } 236 237 // LocalAction implements TestCase.LocalAction. 238 func (*FilterOutputDropUDPOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 239 // Listen for UDP packets on dropPort. 240 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 241 defer cancel() 242 if err := listenUDP(timedCtx, dropPort, ipv6); err == nil { 243 return fmt.Errorf("packets should not be received") 244 } else if !errors.Is(err, context.DeadlineExceeded) { 245 return fmt.Errorf("error reading: %v", err) 246 } 247 248 return nil 249 } 250 251 // FilterOutputOwnerFail tests that without uid/gid option, owner rule 252 // will fail. 253 type FilterOutputOwnerFail struct{ baseCase } 254 255 var _ TestCase = (*FilterOutputOwnerFail)(nil) 256 257 // Name implements TestCase.Name. 258 func (*FilterOutputOwnerFail) Name() string { 259 return "FilterOutputOwnerFail" 260 } 261 262 // ContainerAction implements TestCase.ContainerAction. 263 func (*FilterOutputOwnerFail) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 264 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-m", "owner", "-j", "ACCEPT"); err == nil { 265 return fmt.Errorf("invalid argument") 266 } 267 268 return nil 269 } 270 271 // LocalAction implements TestCase.LocalAction. 272 func (*FilterOutputOwnerFail) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 273 // no-op. 274 return nil 275 } 276 277 // FilterOutputAcceptGIDOwner tests that TCP connections from gid owner are accepted. 278 type FilterOutputAcceptGIDOwner struct{ baseCase } 279 280 var _ TestCase = (*FilterOutputAcceptGIDOwner)(nil) 281 282 // Name implements TestCase.Name. 283 func (*FilterOutputAcceptGIDOwner) Name() string { 284 return "FilterOutputAcceptGIDOwner" 285 } 286 287 // ContainerAction implements TestCase.ContainerAction. 288 func (*FilterOutputAcceptGIDOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 289 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "ACCEPT"); err != nil { 290 return err 291 } 292 293 // Listen for TCP packets on accept port. 294 return listenTCP(ctx, acceptPort, ipv6) 295 } 296 297 // LocalAction implements TestCase.LocalAction. 298 func (*FilterOutputAcceptGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 299 return connectTCP(ctx, ip, acceptPort, ipv6) 300 } 301 302 // FilterOutputDropGIDOwner tests that TCP connections from gid owner are dropped. 303 type FilterOutputDropGIDOwner struct{ baseCase } 304 305 var _ TestCase = (*FilterOutputDropGIDOwner)(nil) 306 307 // Name implements TestCase.Name. 308 func (*FilterOutputDropGIDOwner) Name() string { 309 return "FilterOutputDropGIDOwner" 310 } 311 312 // ContainerAction implements TestCase.ContainerAction. 313 func (*FilterOutputDropGIDOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 314 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "owner", "--gid-owner", "root", "-j", "DROP"); err != nil { 315 return err 316 } 317 318 // Listen for TCP packets on accept port. 319 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 320 defer cancel() 321 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 322 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort) 323 } else if !errors.Is(err, context.DeadlineExceeded) { 324 return fmt.Errorf("error reading: %v", err) 325 } 326 327 return nil 328 } 329 330 // LocalAction implements TestCase.LocalAction. 331 func (*FilterOutputDropGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 332 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 333 defer cancel() 334 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 335 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort) 336 } 337 338 return nil 339 } 340 341 // FilterOutputInvertGIDOwner tests that TCP connections from gid owner are dropped. 342 type FilterOutputInvertGIDOwner struct{ baseCase } 343 344 var _ TestCase = (*FilterOutputInvertGIDOwner)(nil) 345 346 // Name implements TestCase.Name. 347 func (*FilterOutputInvertGIDOwner) Name() string { 348 return "FilterOutputInvertGIDOwner" 349 } 350 351 // ContainerAction implements TestCase.ContainerAction. 352 func (*FilterOutputInvertGIDOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 353 rules := [][]string{ 354 {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--gid-owner", "root", "-j", "ACCEPT"}, 355 {"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"}, 356 } 357 if err := filterTableRules(ipv6, rules); err != nil { 358 return err 359 } 360 361 // Listen for TCP packets on accept port. 362 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 363 defer cancel() 364 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 365 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort) 366 } else if !errors.Is(err, context.DeadlineExceeded) { 367 return fmt.Errorf("error reading: %v", err) 368 } 369 370 return nil 371 } 372 373 // LocalAction implements TestCase.LocalAction. 374 func (*FilterOutputInvertGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 375 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 376 defer cancel() 377 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 378 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort) 379 } 380 381 return nil 382 } 383 384 // FilterOutputInvertUIDOwner tests that TCP connections from gid owner are dropped. 385 type FilterOutputInvertUIDOwner struct{ baseCase } 386 387 var _ TestCase = (*FilterOutputInvertUIDOwner)(nil) 388 389 // Name implements TestCase.Name. 390 func (*FilterOutputInvertUIDOwner) Name() string { 391 return "FilterOutputInvertUIDOwner" 392 } 393 394 // ContainerAction implements TestCase.ContainerAction. 395 func (*FilterOutputInvertUIDOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 396 rules := [][]string{ 397 {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "-j", "DROP"}, 398 {"-A", "OUTPUT", "-p", "tcp", "-j", "ACCEPT"}, 399 } 400 if err := filterTableRules(ipv6, rules); err != nil { 401 return err 402 } 403 404 // Listen for TCP packets on accept port. 405 return listenTCP(ctx, acceptPort, ipv6) 406 } 407 408 // LocalAction implements TestCase.LocalAction. 409 func (*FilterOutputInvertUIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 410 return connectTCP(ctx, ip, acceptPort, ipv6) 411 } 412 413 // FilterOutputInvertUIDAndGIDOwner tests that TCP connections from uid and gid 414 // owner are dropped. 415 type FilterOutputInvertUIDAndGIDOwner struct{ baseCase } 416 417 var _ TestCase = (*FilterOutputInvertUIDAndGIDOwner)(nil) 418 419 // Name implements TestCase.Name. 420 func (*FilterOutputInvertUIDAndGIDOwner) Name() string { 421 return "FilterOutputInvertUIDAndGIDOwner" 422 } 423 424 // ContainerAction implements TestCase.ContainerAction. 425 func (*FilterOutputInvertUIDAndGIDOwner) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 426 rules := [][]string{ 427 {"-A", "OUTPUT", "-p", "tcp", "-m", "owner", "!", "--uid-owner", "root", "!", "--gid-owner", "root", "-j", "ACCEPT"}, 428 {"-A", "OUTPUT", "-p", "tcp", "-j", "DROP"}, 429 } 430 if err := filterTableRules(ipv6, rules); err != nil { 431 return err 432 } 433 434 // Listen for TCP packets on accept port. 435 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 436 defer cancel() 437 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 438 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort) 439 } else if !errors.Is(err, context.DeadlineExceeded) { 440 return fmt.Errorf("error reading: %v", err) 441 } 442 443 return nil 444 } 445 446 // LocalAction implements TestCase.LocalAction. 447 func (*FilterOutputInvertUIDAndGIDOwner) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 448 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 449 defer cancel() 450 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 451 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort) 452 } 453 454 return nil 455 } 456 457 // FilterOutputDestination tests that we can selectively allow packets to 458 // certain destinations. 459 type FilterOutputDestination struct{ localCase } 460 461 var _ TestCase = (*FilterOutputDestination)(nil) 462 463 // Name implements TestCase.Name. 464 func (*FilterOutputDestination) Name() string { 465 return "FilterOutputDestination" 466 } 467 468 // ContainerAction implements TestCase.ContainerAction. 469 func (*FilterOutputDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 470 var rules [][]string 471 if ipv6 { 472 rules = [][]string{ 473 {"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"}, 474 // Allow solicited node multicast addresses so we can send neighbor 475 // solicitations. 476 {"-A", "OUTPUT", "-d", "ff02::1:ff00:0/104", "-j", "ACCEPT"}, 477 {"-P", "OUTPUT", "DROP"}, 478 } 479 } else { 480 rules = [][]string{ 481 {"-A", "OUTPUT", "-d", ip.String(), "-j", "ACCEPT"}, 482 {"-P", "OUTPUT", "DROP"}, 483 } 484 } 485 if err := filterTableRules(ipv6, rules); err != nil { 486 return err 487 } 488 489 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 490 } 491 492 // LocalAction implements TestCase.LocalAction. 493 func (*FilterOutputDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 494 return listenUDP(ctx, acceptPort, ipv6) 495 } 496 497 // FilterOutputInvertDestination tests that we can selectively allow packets 498 // not headed for a particular destination. 499 type FilterOutputInvertDestination struct{ localCase } 500 501 var _ TestCase = (*FilterOutputInvertDestination)(nil) 502 503 // Name implements TestCase.Name. 504 func (*FilterOutputInvertDestination) Name() string { 505 return "FilterOutputInvertDestination" 506 } 507 508 // ContainerAction implements TestCase.ContainerAction. 509 func (*FilterOutputInvertDestination) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 510 rules := [][]string{ 511 {"-A", "OUTPUT", "!", "-d", localIP(ipv6), "-j", "ACCEPT"}, 512 {"-P", "OUTPUT", "DROP"}, 513 } 514 if err := filterTableRules(ipv6, rules); err != nil { 515 return err 516 } 517 518 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 519 } 520 521 // LocalAction implements TestCase.LocalAction. 522 func (*FilterOutputInvertDestination) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 523 return listenUDP(ctx, acceptPort, ipv6) 524 } 525 526 // FilterOutputInterfaceAccept tests that packets are sent via interface 527 // matching the iptables rule. 528 type FilterOutputInterfaceAccept struct{ localCase } 529 530 var _ TestCase = (*FilterOutputInterfaceAccept)(nil) 531 532 // Name implements TestCase.Name. 533 func (*FilterOutputInterfaceAccept) Name() string { 534 return "FilterOutputInterfaceAccept" 535 } 536 537 // ContainerAction implements TestCase.ContainerAction. 538 func (*FilterOutputInterfaceAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 539 ifname, ok := getInterfaceName() 540 if !ok { 541 return fmt.Errorf("no interface is present, except loopback") 542 } 543 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "ACCEPT"); err != nil { 544 return err 545 } 546 547 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 548 } 549 550 // LocalAction implements TestCase.LocalAction. 551 func (*FilterOutputInterfaceAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 552 return listenUDP(ctx, acceptPort, ipv6) 553 } 554 555 // FilterOutputInterfaceDrop tests that packets are not sent via interface 556 // matching the iptables rule. 557 type FilterOutputInterfaceDrop struct{ localCase } 558 559 var _ TestCase = (*FilterOutputInterfaceDrop)(nil) 560 561 // Name implements TestCase.Name. 562 func (*FilterOutputInterfaceDrop) Name() string { 563 return "FilterOutputInterfaceDrop" 564 } 565 566 // ContainerAction implements TestCase.ContainerAction. 567 func (*FilterOutputInterfaceDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 568 ifname, ok := getInterfaceName() 569 if !ok { 570 return fmt.Errorf("no interface is present, except loopback") 571 } 572 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", ifname, "-j", "DROP"); err != nil { 573 return err 574 } 575 576 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 577 } 578 579 // LocalAction implements TestCase.LocalAction. 580 func (*FilterOutputInterfaceDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 581 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 582 defer cancel() 583 if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil { 584 return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort) 585 } else if !errors.Is(err, context.DeadlineExceeded) { 586 return fmt.Errorf("error reading: %v", err) 587 } 588 589 return nil 590 } 591 592 // FilterOutputInterface tests that packets are sent via interface which is 593 // not matching the interface name in the iptables rule. 594 type FilterOutputInterface struct{ localCase } 595 596 var _ TestCase = (*FilterOutputInterface)(nil) 597 598 // Name implements TestCase.Name. 599 func (*FilterOutputInterface) Name() string { 600 return "FilterOutputInterface" 601 } 602 603 // ContainerAction implements TestCase.ContainerAction. 604 func (*FilterOutputInterface) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 605 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "lo", "-j", "DROP"); err != nil { 606 return err 607 } 608 609 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 610 } 611 612 // LocalAction implements TestCase.LocalAction. 613 func (*FilterOutputInterface) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 614 return listenUDP(ctx, acceptPort, ipv6) 615 } 616 617 // FilterOutputInterfaceBeginsWith tests that packets are not sent via an 618 // interface which begins with the given interface name. 619 type FilterOutputInterfaceBeginsWith struct{ localCase } 620 621 var _ TestCase = (*FilterOutputInterfaceBeginsWith)(nil) 622 623 // Name implements TestCase.Name. 624 func (*FilterOutputInterfaceBeginsWith) Name() string { 625 return "FilterOutputInterfaceBeginsWith" 626 } 627 628 // ContainerAction implements TestCase.ContainerAction. 629 func (*FilterOutputInterfaceBeginsWith) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 630 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-o", "e+", "-j", "DROP"); err != nil { 631 return err 632 } 633 634 return sendUDPLoop(ctx, ip, acceptPort, ipv6) 635 } 636 637 // LocalAction implements TestCase.LocalAction. 638 func (*FilterOutputInterfaceBeginsWith) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 639 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 640 defer cancel() 641 if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil { 642 return fmt.Errorf("packets should not be received on port %v, but are received", acceptPort) 643 } else if !errors.Is(err, context.DeadlineExceeded) { 644 return fmt.Errorf("error reading: %v", err) 645 } 646 647 return nil 648 } 649 650 // FilterOutputInterfaceInvertDrop tests that we selectively do not send 651 // packets via interface not matching the interface name. 652 type FilterOutputInterfaceInvertDrop struct{ baseCase } 653 654 var _ TestCase = (*FilterOutputInterfaceInvertDrop)(nil) 655 656 // Name implements TestCase.Name. 657 func (*FilterOutputInterfaceInvertDrop) Name() string { 658 return "FilterOutputInterfaceInvertDrop" 659 } 660 661 // ContainerAction implements TestCase.ContainerAction. 662 func (*FilterOutputInterfaceInvertDrop) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 663 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "DROP"); err != nil { 664 return err 665 } 666 667 // Listen for TCP packets on accept port. 668 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 669 defer cancel() 670 if err := listenTCP(timedCtx, acceptPort, ipv6); err == nil { 671 return fmt.Errorf("connection on port %d should not be accepted, but got accepted", acceptPort) 672 } else if !errors.Is(err, context.DeadlineExceeded) { 673 return fmt.Errorf("error reading: %v", err) 674 } 675 676 return nil 677 } 678 679 // LocalAction implements TestCase.LocalAction. 680 func (*FilterOutputInterfaceInvertDrop) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 681 timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout) 682 defer cancel() 683 if err := connectTCP(timedCtx, ip, acceptPort, ipv6); err == nil { 684 return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", acceptPort) 685 } 686 687 return nil 688 } 689 690 // FilterOutputInterfaceInvertAccept tests that we can selectively send packets 691 // not matching the specific outgoing interface. 692 type FilterOutputInterfaceInvertAccept struct{ baseCase } 693 694 var _ TestCase = (*FilterOutputInterfaceInvertAccept)(nil) 695 696 // Name implements TestCase.Name. 697 func (*FilterOutputInterfaceInvertAccept) Name() string { 698 return "FilterOutputInterfaceInvertAccept" 699 } 700 701 // ContainerAction implements TestCase.ContainerAction. 702 func (*FilterOutputInterfaceInvertAccept) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error { 703 if err := filterTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "!", "-o", "lo", "-j", "ACCEPT"); err != nil { 704 return err 705 } 706 707 // Listen for TCP packets on accept port. 708 return listenTCP(ctx, acceptPort, ipv6) 709 } 710 711 // LocalAction implements TestCase.LocalAction. 712 func (*FilterOutputInterfaceInvertAccept) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error { 713 return connectTCP(ctx, ip, acceptPort, ipv6) 714 }