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  }