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