github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/iptables/nat.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  	"strconv"
    23  
    24  	"golang.org/x/sys/unix"
    25  	"github.com/SagerNet/gvisor/pkg/binary"
    26  	"github.com/SagerNet/gvisor/pkg/hostarch"
    27  )
    28  
    29  const redirectPort = 42
    30  
    31  func init() {
    32  	RegisterTestCase(&NATPreRedirectUDPPort{})
    33  	RegisterTestCase(&NATPreRedirectTCPPort{})
    34  	RegisterTestCase(&NATPreRedirectTCPOutgoing{})
    35  	RegisterTestCase(&NATOutRedirectTCPIncoming{})
    36  	RegisterTestCase(&NATOutRedirectUDPPort{})
    37  	RegisterTestCase(&NATOutRedirectTCPPort{})
    38  	RegisterTestCase(&NATDropUDP{})
    39  	RegisterTestCase(&NATAcceptAll{})
    40  	RegisterTestCase(&NATPreRedirectIP{})
    41  	RegisterTestCase(&NATPreDontRedirectIP{})
    42  	RegisterTestCase(&NATPreRedirectInvert{})
    43  	RegisterTestCase(&NATOutRedirectIP{})
    44  	RegisterTestCase(&NATOutDontRedirectIP{})
    45  	RegisterTestCase(&NATOutRedirectInvert{})
    46  	RegisterTestCase(&NATRedirectRequiresProtocol{})
    47  	RegisterTestCase(&NATLoopbackSkipsPrerouting{})
    48  	RegisterTestCase(&NATPreOriginalDst{})
    49  	RegisterTestCase(&NATOutOriginalDst{})
    50  	RegisterTestCase(&NATPreRECVORIGDSTADDR{})
    51  	RegisterTestCase(&NATOutRECVORIGDSTADDR{})
    52  	RegisterTestCase(&NATPostSNATUDP{})
    53  	RegisterTestCase(&NATPostSNATTCP{})
    54  }
    55  
    56  // NATPreRedirectUDPPort tests that packets are redirected to different port.
    57  type NATPreRedirectUDPPort struct{ containerCase }
    58  
    59  var _ TestCase = (*NATPreRedirectUDPPort)(nil)
    60  
    61  // Name implements TestCase.Name.
    62  func (*NATPreRedirectUDPPort) Name() string {
    63  	return "NATPreRedirectUDPPort"
    64  }
    65  
    66  // ContainerAction implements TestCase.ContainerAction.
    67  func (*NATPreRedirectUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
    68  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
    69  		return err
    70  	}
    71  
    72  	if err := listenUDP(ctx, redirectPort, ipv6); err != nil {
    73  		return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", redirectPort, err)
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  // LocalAction implements TestCase.LocalAction.
    80  func (*NATPreRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
    81  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
    82  }
    83  
    84  // NATPreRedirectTCPPort tests that connections are redirected on specified ports.
    85  type NATPreRedirectTCPPort struct{ baseCase }
    86  
    87  var _ TestCase = (*NATPreRedirectTCPPort)(nil)
    88  
    89  // Name implements TestCase.Name.
    90  func (*NATPreRedirectTCPPort) Name() string {
    91  	return "NATPreRedirectTCPPort"
    92  }
    93  
    94  // ContainerAction implements TestCase.ContainerAction.
    95  func (*NATPreRedirectTCPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
    96  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
    97  		return err
    98  	}
    99  
   100  	// Listen for TCP packets on redirect port.
   101  	return listenTCP(ctx, acceptPort, ipv6)
   102  }
   103  
   104  // LocalAction implements TestCase.LocalAction.
   105  func (*NATPreRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   106  	return connectTCP(ctx, ip, dropPort, ipv6)
   107  }
   108  
   109  // NATPreRedirectTCPOutgoing verifies that outgoing TCP connections aren't
   110  // affected by PREROUTING connection tracking.
   111  type NATPreRedirectTCPOutgoing struct{ baseCase }
   112  
   113  var _ TestCase = (*NATPreRedirectTCPOutgoing)(nil)
   114  
   115  // Name implements TestCase.Name.
   116  func (*NATPreRedirectTCPOutgoing) Name() string {
   117  	return "NATPreRedirectTCPOutgoing"
   118  }
   119  
   120  // ContainerAction implements TestCase.ContainerAction.
   121  func (*NATPreRedirectTCPOutgoing) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   122  	// Redirect all incoming TCP traffic to a closed port.
   123  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
   124  		return err
   125  	}
   126  
   127  	// Establish a connection to the host process.
   128  	return connectTCP(ctx, ip, acceptPort, ipv6)
   129  }
   130  
   131  // LocalAction implements TestCase.LocalAction.
   132  func (*NATPreRedirectTCPOutgoing) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   133  	return listenTCP(ctx, acceptPort, ipv6)
   134  }
   135  
   136  // NATOutRedirectTCPIncoming verifies that incoming TCP connections aren't
   137  // affected by OUTPUT connection tracking.
   138  type NATOutRedirectTCPIncoming struct{ baseCase }
   139  
   140  var _ TestCase = (*NATOutRedirectTCPIncoming)(nil)
   141  
   142  // Name implements TestCase.Name.
   143  func (*NATOutRedirectTCPIncoming) Name() string {
   144  	return "NATOutRedirectTCPIncoming"
   145  }
   146  
   147  // ContainerAction implements TestCase.ContainerAction.
   148  func (*NATOutRedirectTCPIncoming) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   149  	// Redirect all outgoing TCP traffic to a closed port.
   150  	if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
   151  		return err
   152  	}
   153  
   154  	// Establish a connection to the host process.
   155  	return listenTCP(ctx, acceptPort, ipv6)
   156  }
   157  
   158  // LocalAction implements TestCase.LocalAction.
   159  func (*NATOutRedirectTCPIncoming) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   160  	return connectTCP(ctx, ip, acceptPort, ipv6)
   161  }
   162  
   163  // NATOutRedirectUDPPort tests that packets are redirected to different port.
   164  type NATOutRedirectUDPPort struct{ containerCase }
   165  
   166  var _ TestCase = (*NATOutRedirectUDPPort)(nil)
   167  
   168  // Name implements TestCase.Name.
   169  func (*NATOutRedirectUDPPort) Name() string {
   170  	return "NATOutRedirectUDPPort"
   171  }
   172  
   173  // ContainerAction implements TestCase.ContainerAction.
   174  func (*NATOutRedirectUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   175  	return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)), "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort))
   176  }
   177  
   178  // LocalAction implements TestCase.LocalAction.
   179  func (*NATOutRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   180  	// No-op.
   181  	return nil
   182  }
   183  
   184  // NATDropUDP tests that packets are not received in ports other than redirect
   185  // port.
   186  type NATDropUDP struct{ containerCase }
   187  
   188  var _ TestCase = (*NATDropUDP)(nil)
   189  
   190  // Name implements TestCase.Name.
   191  func (*NATDropUDP) Name() string {
   192  	return "NATDropUDP"
   193  }
   194  
   195  // ContainerAction implements TestCase.ContainerAction.
   196  func (*NATDropUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   197  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
   198  		return err
   199  	}
   200  
   201  	timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
   202  	defer cancel()
   203  	if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil {
   204  		return fmt.Errorf("packets on port %d should have been redirected to port %d", acceptPort, redirectPort)
   205  	} else if !errors.Is(err, context.DeadlineExceeded) {
   206  		return fmt.Errorf("error reading: %v", err)
   207  	}
   208  
   209  	return nil
   210  }
   211  
   212  // LocalAction implements TestCase.LocalAction.
   213  func (*NATDropUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   214  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   215  }
   216  
   217  // NATAcceptAll tests that all UDP packets are accepted.
   218  type NATAcceptAll struct{ containerCase }
   219  
   220  var _ TestCase = (*NATAcceptAll)(nil)
   221  
   222  // Name implements TestCase.Name.
   223  func (*NATAcceptAll) Name() string {
   224  	return "NATAcceptAll"
   225  }
   226  
   227  // ContainerAction implements TestCase.ContainerAction.
   228  func (*NATAcceptAll) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   229  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil {
   230  		return err
   231  	}
   232  
   233  	if err := listenUDP(ctx, acceptPort, ipv6); err != nil {
   234  		return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", acceptPort, err)
   235  	}
   236  
   237  	return nil
   238  }
   239  
   240  // LocalAction implements TestCase.LocalAction.
   241  func (*NATAcceptAll) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   242  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   243  }
   244  
   245  // NATOutRedirectIP uses iptables to select packets based on destination IP and
   246  // redirects them.
   247  type NATOutRedirectIP struct{ baseCase }
   248  
   249  var _ TestCase = (*NATOutRedirectIP)(nil)
   250  
   251  // Name implements TestCase.Name.
   252  func (*NATOutRedirectIP) Name() string {
   253  	return "NATOutRedirectIP"
   254  }
   255  
   256  // ContainerAction implements TestCase.ContainerAction.
   257  func (*NATOutRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   258  	// Redirect OUTPUT packets to a listening localhost port.
   259  	return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)),
   260  		"-A", "OUTPUT",
   261  		"-d", nowhereIP(ipv6),
   262  		"-p", "udp",
   263  		"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
   264  }
   265  
   266  // LocalAction implements TestCase.LocalAction.
   267  func (*NATOutRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   268  	// No-op.
   269  	return nil
   270  }
   271  
   272  // NATOutDontRedirectIP tests that iptables matching with "-d" does not match
   273  // packets it shouldn't.
   274  type NATOutDontRedirectIP struct{ localCase }
   275  
   276  var _ TestCase = (*NATOutDontRedirectIP)(nil)
   277  
   278  // Name implements TestCase.Name.
   279  func (*NATOutDontRedirectIP) Name() string {
   280  	return "NATOutDontRedirectIP"
   281  }
   282  
   283  // ContainerAction implements TestCase.ContainerAction.
   284  func (*NATOutDontRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   285  	if err := natTable(ipv6, "-A", "OUTPUT", "-d", localIP(ipv6), "-p", "udp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
   286  		return err
   287  	}
   288  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   289  }
   290  
   291  // LocalAction implements TestCase.LocalAction.
   292  func (*NATOutDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   293  	return listenUDP(ctx, acceptPort, ipv6)
   294  }
   295  
   296  // NATOutRedirectInvert tests that iptables can match with "! -d".
   297  type NATOutRedirectInvert struct{ baseCase }
   298  
   299  var _ TestCase = (*NATOutRedirectInvert)(nil)
   300  
   301  // Name implements TestCase.Name.
   302  func (*NATOutRedirectInvert) Name() string {
   303  	return "NATOutRedirectInvert"
   304  }
   305  
   306  // ContainerAction implements TestCase.ContainerAction.
   307  func (*NATOutRedirectInvert) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   308  	// Redirect OUTPUT packets to a listening localhost port.
   309  	dest := "192.0.2.2"
   310  	if ipv6 {
   311  		dest = "2001:db8::2"
   312  	}
   313  	return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)),
   314  		"-A", "OUTPUT",
   315  		"!", "-d", dest,
   316  		"-p", "udp",
   317  		"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
   318  }
   319  
   320  // LocalAction implements TestCase.LocalAction.
   321  func (*NATOutRedirectInvert) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   322  	// No-op.
   323  	return nil
   324  }
   325  
   326  // NATPreRedirectIP tests that we can use iptables to select packets based on
   327  // destination IP and redirect them.
   328  type NATPreRedirectIP struct{ containerCase }
   329  
   330  var _ TestCase = (*NATPreRedirectIP)(nil)
   331  
   332  // Name implements TestCase.Name.
   333  func (*NATPreRedirectIP) Name() string {
   334  	return "NATPreRedirectIP"
   335  }
   336  
   337  // ContainerAction implements TestCase.ContainerAction.
   338  func (*NATPreRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   339  	addrs, err := localAddrs(ipv6)
   340  	if err != nil {
   341  		return err
   342  	}
   343  
   344  	var rules [][]string
   345  	for _, addr := range addrs {
   346  		rules = append(rules, []string{"-A", "PREROUTING", "-p", "udp", "-d", addr, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)})
   347  	}
   348  	if err := natTableRules(ipv6, rules); err != nil {
   349  		return err
   350  	}
   351  	return listenUDP(ctx, acceptPort, ipv6)
   352  }
   353  
   354  // LocalAction implements TestCase.LocalAction.
   355  func (*NATPreRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   356  	return sendUDPLoop(ctx, ip, dropPort, ipv6)
   357  }
   358  
   359  // NATPreDontRedirectIP tests that iptables matching with "-d" does not match
   360  // packets it shouldn't.
   361  type NATPreDontRedirectIP struct{ containerCase }
   362  
   363  var _ TestCase = (*NATPreDontRedirectIP)(nil)
   364  
   365  // Name implements TestCase.Name.
   366  func (*NATPreDontRedirectIP) Name() string {
   367  	return "NATPreDontRedirectIP"
   368  }
   369  
   370  // ContainerAction implements TestCase.ContainerAction.
   371  func (*NATPreDontRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   372  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
   373  		return err
   374  	}
   375  	return listenUDP(ctx, acceptPort, ipv6)
   376  }
   377  
   378  // LocalAction implements TestCase.LocalAction.
   379  func (*NATPreDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   380  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   381  }
   382  
   383  // NATPreRedirectInvert tests that iptables can match with "! -d".
   384  type NATPreRedirectInvert struct{ containerCase }
   385  
   386  var _ TestCase = (*NATPreRedirectInvert)(nil)
   387  
   388  // Name implements TestCase.Name.
   389  func (*NATPreRedirectInvert) Name() string {
   390  	return "NATPreRedirectInvert"
   391  }
   392  
   393  // ContainerAction implements TestCase.ContainerAction.
   394  func (*NATPreRedirectInvert) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   395  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "!", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
   396  		return err
   397  	}
   398  	return listenUDP(ctx, acceptPort, ipv6)
   399  }
   400  
   401  // LocalAction implements TestCase.LocalAction.
   402  func (*NATPreRedirectInvert) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   403  	return sendUDPLoop(ctx, ip, dropPort, ipv6)
   404  }
   405  
   406  // NATRedirectRequiresProtocol tests that use of the --to-ports flag requires a
   407  // protocol to be specified with -p.
   408  type NATRedirectRequiresProtocol struct{ baseCase }
   409  
   410  var _ TestCase = (*NATRedirectRequiresProtocol)(nil)
   411  
   412  // Name implements TestCase.Name.
   413  func (*NATRedirectRequiresProtocol) Name() string {
   414  	return "NATRedirectRequiresProtocol"
   415  }
   416  
   417  // ContainerAction implements TestCase.ContainerAction.
   418  func (*NATRedirectRequiresProtocol) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   419  	if err := natTable(ipv6, "-A", "PREROUTING", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err == nil {
   420  		return errors.New("expected an error using REDIRECT --to-ports without a protocol")
   421  	}
   422  	return nil
   423  }
   424  
   425  // LocalAction implements TestCase.LocalAction.
   426  func (*NATRedirectRequiresProtocol) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   427  	// No-op.
   428  	return nil
   429  }
   430  
   431  // NATOutRedirectTCPPort tests that connections are redirected on specified ports.
   432  type NATOutRedirectTCPPort struct{ baseCase }
   433  
   434  var _ TestCase = (*NATOutRedirectTCPPort)(nil)
   435  
   436  // Name implements TestCase.Name.
   437  func (*NATOutRedirectTCPPort) Name() string {
   438  	return "NATOutRedirectTCPPort"
   439  }
   440  
   441  // ContainerAction implements TestCase.ContainerAction.
   442  func (*NATOutRedirectTCPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   443  	if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
   444  		return err
   445  	}
   446  
   447  	localAddr := net.TCPAddr{
   448  		IP:   net.ParseIP(localIP(ipv6)),
   449  		Port: acceptPort,
   450  	}
   451  
   452  	// Starts listening on port.
   453  	lConn, err := net.ListenTCP("tcp", &localAddr)
   454  	if err != nil {
   455  		return err
   456  	}
   457  	defer lConn.Close()
   458  
   459  	// Accept connections on port.
   460  	if err := connectTCP(ctx, ip, dropPort, ipv6); err != nil {
   461  		return err
   462  	}
   463  
   464  	conn, err := lConn.AcceptTCP()
   465  	if err != nil {
   466  		return err
   467  	}
   468  	conn.Close()
   469  
   470  	return nil
   471  }
   472  
   473  // LocalAction implements TestCase.LocalAction.
   474  func (*NATOutRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   475  	return nil
   476  }
   477  
   478  // NATLoopbackSkipsPrerouting tests that packets sent via loopback aren't
   479  // affected by PREROUTING rules.
   480  type NATLoopbackSkipsPrerouting struct{ baseCase }
   481  
   482  var _ TestCase = (*NATLoopbackSkipsPrerouting)(nil)
   483  
   484  // Name implements TestCase.Name.
   485  func (*NATLoopbackSkipsPrerouting) Name() string {
   486  	return "NATLoopbackSkipsPrerouting"
   487  }
   488  
   489  // ContainerAction implements TestCase.ContainerAction.
   490  func (*NATLoopbackSkipsPrerouting) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   491  	// Redirect anything sent to localhost to an unused port.
   492  	var dest net.IP
   493  	if ipv6 {
   494  		dest = net.IPv6loopback
   495  	} else {
   496  		dest = net.IPv4(127, 0, 0, 1)
   497  	}
   498  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
   499  		return err
   500  	}
   501  
   502  	// Establish a connection via localhost. If the PREROUTING rule did apply to
   503  	// loopback traffic, the connection would fail.
   504  	sendCh := make(chan error)
   505  	go func() {
   506  		sendCh <- connectTCP(ctx, dest, acceptPort, ipv6)
   507  	}()
   508  
   509  	if err := listenTCP(ctx, acceptPort, ipv6); err != nil {
   510  		return err
   511  	}
   512  	return <-sendCh
   513  }
   514  
   515  // LocalAction implements TestCase.LocalAction.
   516  func (*NATLoopbackSkipsPrerouting) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   517  	// No-op.
   518  	return nil
   519  }
   520  
   521  // NATPreOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
   522  // of PREROUTING NATted packets.
   523  type NATPreOriginalDst struct{ baseCase }
   524  
   525  var _ TestCase = (*NATPreOriginalDst)(nil)
   526  
   527  // Name implements TestCase.Name.
   528  func (*NATPreOriginalDst) Name() string {
   529  	return "NATPreOriginalDst"
   530  }
   531  
   532  // ContainerAction implements TestCase.ContainerAction.
   533  func (*NATPreOriginalDst) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   534  	// Redirect incoming TCP connections to acceptPort.
   535  	if err := natTable(ipv6, "-A", "PREROUTING",
   536  		"-p", "tcp",
   537  		"--destination-port", fmt.Sprintf("%d", dropPort),
   538  		"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
   539  		return err
   540  	}
   541  
   542  	addrs, err := getInterfaceAddrs(ipv6)
   543  	if err != nil {
   544  		return err
   545  	}
   546  	return listenForRedirectedConn(ctx, ipv6, addrs)
   547  }
   548  
   549  // LocalAction implements TestCase.LocalAction.
   550  func (*NATPreOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   551  	return connectTCP(ctx, ip, dropPort, ipv6)
   552  }
   553  
   554  // NATOutOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
   555  // of OUTBOUND NATted packets.
   556  type NATOutOriginalDst struct{ baseCase }
   557  
   558  var _ TestCase = (*NATOutOriginalDst)(nil)
   559  
   560  // Name implements TestCase.Name.
   561  func (*NATOutOriginalDst) Name() string {
   562  	return "NATOutOriginalDst"
   563  }
   564  
   565  // ContainerAction implements TestCase.ContainerAction.
   566  func (*NATOutOriginalDst) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   567  	// Redirect incoming TCP connections to acceptPort.
   568  	if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
   569  		return err
   570  	}
   571  
   572  	connCh := make(chan error)
   573  	go func() {
   574  		connCh <- connectTCP(ctx, ip, dropPort, ipv6)
   575  	}()
   576  
   577  	if err := listenForRedirectedConn(ctx, ipv6, []net.IP{ip}); err != nil {
   578  		return err
   579  	}
   580  	return <-connCh
   581  }
   582  
   583  // LocalAction implements TestCase.LocalAction.
   584  func (*NATOutOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   585  	// No-op.
   586  	return nil
   587  }
   588  
   589  func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.IP) error {
   590  	// The net package doesn't give guaranteed access to the connection's
   591  	// underlying FD, and thus we cannot call getsockopt. We have to use
   592  	// traditional syscalls.
   593  
   594  	// Create the listening socket, bind, listen, and accept.
   595  	family := unix.AF_INET
   596  	if ipv6 {
   597  		family = unix.AF_INET6
   598  	}
   599  	sockfd, err := unix.Socket(family, unix.SOCK_STREAM, 0)
   600  	if err != nil {
   601  		return err
   602  	}
   603  	defer unix.Close(sockfd)
   604  
   605  	var bindAddr unix.Sockaddr
   606  	if ipv6 {
   607  		bindAddr = &unix.SockaddrInet6{
   608  			Port: acceptPort,
   609  			Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
   610  		}
   611  	} else {
   612  		bindAddr = &unix.SockaddrInet4{
   613  			Port: acceptPort,
   614  			Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
   615  		}
   616  	}
   617  	if err := unix.Bind(sockfd, bindAddr); err != nil {
   618  		return err
   619  	}
   620  
   621  	if err := unix.Listen(sockfd, 1); err != nil {
   622  		return err
   623  	}
   624  
   625  	// Block on accept() in another goroutine.
   626  	connCh := make(chan int)
   627  	errCh := make(chan error)
   628  	go func() {
   629  		for {
   630  			connFD, _, err := unix.Accept(sockfd)
   631  			if errors.Is(err, unix.EINTR) {
   632  				continue
   633  			}
   634  			if err != nil {
   635  				errCh <- err
   636  				return
   637  			}
   638  			connCh <- connFD
   639  			return
   640  		}
   641  	}()
   642  
   643  	// Wait for accept() to return or for the context to finish.
   644  	var connFD int
   645  	select {
   646  	case <-ctx.Done():
   647  		return ctx.Err()
   648  	case err := <-errCh:
   649  		return err
   650  	case connFD = <-connCh:
   651  	}
   652  	defer unix.Close(connFD)
   653  
   654  	// Verify that, despite listening on acceptPort, SO_ORIGINAL_DST
   655  	// indicates the packet was sent to originalDst:dropPort.
   656  	if ipv6 {
   657  		got, err := originalDestination6(connFD)
   658  		if err != nil {
   659  			return err
   660  		}
   661  		return addrMatches6(got, originalDsts, dropPort)
   662  	}
   663  
   664  	got, err := originalDestination4(connFD)
   665  	if err != nil {
   666  		return err
   667  	}
   668  	return addrMatches4(got, originalDsts, dropPort)
   669  }
   670  
   671  // loopbackTests runs an iptables rule and ensures that packets sent to
   672  // dest:dropPort are received by localhost:acceptPort.
   673  func loopbackTest(ctx context.Context, ipv6 bool, dest net.IP, args ...string) error {
   674  	if err := natTable(ipv6, args...); err != nil {
   675  		return err
   676  	}
   677  	sendCh := make(chan error, 1)
   678  	listenCh := make(chan error, 1)
   679  	go func() {
   680  		sendCh <- sendUDPLoop(ctx, dest, dropPort, ipv6)
   681  	}()
   682  	go func() {
   683  		listenCh <- listenUDP(ctx, acceptPort, ipv6)
   684  	}()
   685  	select {
   686  	case err := <-listenCh:
   687  		return err
   688  	case err := <-sendCh:
   689  		return err
   690  	}
   691  }
   692  
   693  // NATPreRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
   694  // address on the PREROUTING chain.
   695  type NATPreRECVORIGDSTADDR struct{ containerCase }
   696  
   697  var _ TestCase = (*NATPreRECVORIGDSTADDR)(nil)
   698  
   699  // Name implements TestCase.Name.
   700  func (*NATPreRECVORIGDSTADDR) Name() string {
   701  	return "NATPreRECVORIGDSTADDR"
   702  }
   703  
   704  // ContainerAction implements TestCase.ContainerAction.
   705  func (*NATPreRECVORIGDSTADDR) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   706  	if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
   707  		return err
   708  	}
   709  
   710  	if err := recvWithRECVORIGDSTADDR(ctx, ipv6, nil, redirectPort); err != nil {
   711  		return err
   712  	}
   713  
   714  	return nil
   715  }
   716  
   717  // LocalAction implements TestCase.LocalAction.
   718  func (*NATPreRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   719  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   720  }
   721  
   722  // NATOutRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
   723  // address on the OUTPUT chain.
   724  type NATOutRECVORIGDSTADDR struct{ containerCase }
   725  
   726  var _ TestCase = (*NATOutRECVORIGDSTADDR)(nil)
   727  
   728  // Name implements TestCase.Name.
   729  func (*NATOutRECVORIGDSTADDR) Name() string {
   730  	return "NATOutRECVORIGDSTADDR"
   731  }
   732  
   733  // ContainerAction implements TestCase.ContainerAction.
   734  func (*NATOutRECVORIGDSTADDR) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   735  	if err := natTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
   736  		return err
   737  	}
   738  
   739  	sendCh := make(chan error)
   740  	go func() {
   741  		// Packets will be sent to a non-container IP and redirected
   742  		// back to the container.
   743  		sendCh <- sendUDPLoop(ctx, ip, acceptPort, ipv6)
   744  	}()
   745  
   746  	expectedIP := &net.IP{127, 0, 0, 1}
   747  	if ipv6 {
   748  		expectedIP = &net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
   749  	}
   750  	if err := recvWithRECVORIGDSTADDR(ctx, ipv6, expectedIP, redirectPort); err != nil {
   751  		return err
   752  	}
   753  
   754  	select {
   755  	case err := <-sendCh:
   756  		return err
   757  	default:
   758  		return nil
   759  	}
   760  }
   761  
   762  // LocalAction implements TestCase.LocalAction.
   763  func (*NATOutRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   764  	// No-op.
   765  	return nil
   766  }
   767  
   768  func recvWithRECVORIGDSTADDR(ctx context.Context, ipv6 bool, expectedDst *net.IP, port uint16) error {
   769  	// The net package doesn't give guaranteed access to a connection's
   770  	// underlying FD, and thus we cannot call getsockopt. We have to use
   771  	// traditional syscalls for IP_RECVORIGDSTADDR.
   772  
   773  	// Create the listening socket.
   774  	var (
   775  		family                 = unix.AF_INET
   776  		level                  = unix.SOL_IP
   777  		option                 = unix.IP_RECVORIGDSTADDR
   778  		bindAddr unix.Sockaddr = &unix.SockaddrInet4{
   779  			Port: int(port),
   780  			Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
   781  		}
   782  	)
   783  	if ipv6 {
   784  		family = unix.AF_INET6
   785  		level = unix.SOL_IPV6
   786  		option = 74 // IPV6_RECVORIGDSTADDR, which is missing from the syscall package.
   787  		bindAddr = &unix.SockaddrInet6{
   788  			Port: int(port),
   789  			Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
   790  		}
   791  	}
   792  	sockfd, err := unix.Socket(family, unix.SOCK_DGRAM, 0)
   793  	if err != nil {
   794  		return fmt.Errorf("failed Socket(%d, %d, 0): %w", family, unix.SOCK_DGRAM, err)
   795  	}
   796  	defer unix.Close(sockfd)
   797  
   798  	if err := unix.Bind(sockfd, bindAddr); err != nil {
   799  		return fmt.Errorf("failed Bind(%d, %+v): %v", sockfd, bindAddr, err)
   800  	}
   801  
   802  	// Enable IP_RECVORIGDSTADDR.
   803  	if err := unix.SetsockoptInt(sockfd, level, option, 1); err != nil {
   804  		return fmt.Errorf("failed SetsockoptByte(%d, %d, %d, 1): %v", sockfd, level, option, err)
   805  	}
   806  
   807  	addrCh := make(chan interface{})
   808  	errCh := make(chan error)
   809  	go func() {
   810  		var addr interface{}
   811  		var err error
   812  		if ipv6 {
   813  			addr, err = recvOrigDstAddr6(sockfd)
   814  		} else {
   815  			addr, err = recvOrigDstAddr4(sockfd)
   816  		}
   817  		if err != nil {
   818  			errCh <- err
   819  		} else {
   820  			addrCh <- addr
   821  		}
   822  	}()
   823  
   824  	// Wait to receive a packet.
   825  	var addr interface{}
   826  	select {
   827  	case <-ctx.Done():
   828  		return ctx.Err()
   829  	case err := <-errCh:
   830  		return err
   831  	case addr = <-addrCh:
   832  	}
   833  
   834  	// Get a list of local IPs to verify that the packet now appears to have
   835  	// been sent to us.
   836  	var localAddrs []net.IP
   837  	if expectedDst != nil {
   838  		localAddrs = []net.IP{*expectedDst}
   839  	} else {
   840  		localAddrs, err = getInterfaceAddrs(ipv6)
   841  		if err != nil {
   842  			return fmt.Errorf("failed to get local interfaces: %w", err)
   843  		}
   844  	}
   845  
   846  	// Verify that the address has the post-NAT port and address.
   847  	if ipv6 {
   848  		return addrMatches6(addr.(unix.RawSockaddrInet6), localAddrs, redirectPort)
   849  	}
   850  	return addrMatches4(addr.(unix.RawSockaddrInet4), localAddrs, redirectPort)
   851  }
   852  
   853  func recvOrigDstAddr4(sockfd int) (unix.RawSockaddrInet4, error) {
   854  	buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet4)
   855  	if err != nil {
   856  		return unix.RawSockaddrInet4{}, err
   857  	}
   858  	var addr unix.RawSockaddrInet4
   859  	binary.Unmarshal(buf, hostarch.ByteOrder, &addr)
   860  	return addr, nil
   861  }
   862  
   863  func recvOrigDstAddr6(sockfd int) (unix.RawSockaddrInet6, error) {
   864  	buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet6)
   865  	if err != nil {
   866  		return unix.RawSockaddrInet6{}, err
   867  	}
   868  	var addr unix.RawSockaddrInet6
   869  	binary.Unmarshal(buf, hostarch.ByteOrder, &addr)
   870  	return addr, nil
   871  }
   872  
   873  func recvOrigDstAddr(sockfd int, level uintptr, addrSize int) ([]byte, error) {
   874  	buf := make([]byte, 64)
   875  	oob := make([]byte, unix.CmsgSpace(addrSize))
   876  	for {
   877  		_, oobn, _, _, err := unix.Recvmsg(
   878  			sockfd,
   879  			buf, // Message buffer.
   880  			oob, // Out-of-band buffer.
   881  			0)   // Flags.
   882  		if errors.Is(err, unix.EINTR) {
   883  			continue
   884  		}
   885  		if err != nil {
   886  			return nil, fmt.Errorf("failed when calling Recvmsg: %w", err)
   887  		}
   888  		oob = oob[:oobn]
   889  
   890  		// Parse out the control message.
   891  		msgs, err := unix.ParseSocketControlMessage(oob)
   892  		if err != nil {
   893  			return nil, fmt.Errorf("failed to parse control message: %w", err)
   894  		}
   895  		return msgs[0].Data, nil
   896  	}
   897  }
   898  
   899  func addrMatches4(got unix.RawSockaddrInet4, wantAddrs []net.IP, port uint16) error {
   900  	for _, wantAddr := range wantAddrs {
   901  		want := unix.RawSockaddrInet4{
   902  			Family: unix.AF_INET,
   903  			Port:   htons(port),
   904  		}
   905  		copy(want.Addr[:], wantAddr.To4())
   906  		if got == want {
   907  			return nil
   908  		}
   909  	}
   910  	return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
   911  }
   912  
   913  func addrMatches6(got unix.RawSockaddrInet6, wantAddrs []net.IP, port uint16) error {
   914  	for _, wantAddr := range wantAddrs {
   915  		want := unix.RawSockaddrInet6{
   916  			Family: unix.AF_INET6,
   917  			Port:   htons(port),
   918  		}
   919  		copy(want.Addr[:], wantAddr.To16())
   920  		if got == want {
   921  			return nil
   922  		}
   923  	}
   924  	return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
   925  }
   926  
   927  const (
   928  	snatAddrV4 = "194.236.50.155"
   929  	snatAddrV6 = "2a0a::1"
   930  	snatPort   = 43
   931  )
   932  
   933  // NATPostSNATUDP tests that the source port/IP in the packets are modified as expected.
   934  type NATPostSNATUDP struct{ localCase }
   935  
   936  var _ TestCase = (*NATPostSNATUDP)(nil)
   937  
   938  // Name implements TestCase.Name.
   939  func (*NATPostSNATUDP) Name() string {
   940  	return "NATPostSNATUDP"
   941  }
   942  
   943  // ContainerAction implements TestCase.ContainerAction.
   944  func (*NATPostSNATUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   945  	var source string
   946  	if ipv6 {
   947  		source = fmt.Sprintf("[%s]:%d", snatAddrV6, snatPort)
   948  	} else {
   949  		source = fmt.Sprintf("%s:%d", snatAddrV4, snatPort)
   950  	}
   951  
   952  	if err := natTable(ipv6, "-A", "POSTROUTING", "-p", "udp", "-j", "SNAT", "--to-source", source); err != nil {
   953  		return err
   954  	}
   955  	return sendUDPLoop(ctx, ip, acceptPort, ipv6)
   956  }
   957  
   958  // LocalAction implements TestCase.LocalAction.
   959  func (*NATPostSNATUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   960  	remote, err := listenUDPFrom(ctx, acceptPort, ipv6)
   961  	if err != nil {
   962  		return err
   963  	}
   964  	var snatAddr string
   965  	if ipv6 {
   966  		snatAddr = snatAddrV6
   967  	} else {
   968  		snatAddr = snatAddrV4
   969  	}
   970  	if got, want := remote.IP, net.ParseIP(snatAddr); !got.Equal(want) {
   971  		return fmt.Errorf("got remote address = %s, want = %s", got, want)
   972  	}
   973  	if got, want := remote.Port, snatPort; got != want {
   974  		return fmt.Errorf("got remote port = %d, want = %d", got, want)
   975  	}
   976  	return nil
   977  }
   978  
   979  // NATPostSNATTCP tests that the source port/IP in the packets are modified as
   980  // expected.
   981  type NATPostSNATTCP struct{ localCase }
   982  
   983  var _ TestCase = (*NATPostSNATTCP)(nil)
   984  
   985  // Name implements TestCase.Name.
   986  func (*NATPostSNATTCP) Name() string {
   987  	return "NATPostSNATTCP"
   988  }
   989  
   990  // ContainerAction implements TestCase.ContainerAction.
   991  func (*NATPostSNATTCP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
   992  	addrs, err := getInterfaceAddrs(ipv6)
   993  	if err != nil {
   994  		return err
   995  	}
   996  	var source string
   997  	for _, addr := range addrs {
   998  		if addr.To4() != nil {
   999  			if !ipv6 {
  1000  				source = fmt.Sprintf("%s:%d", addr, snatPort)
  1001  			}
  1002  		} else if ipv6 && addr.IsGlobalUnicast() {
  1003  			source = fmt.Sprintf("[%s]:%d", addr, snatPort)
  1004  		}
  1005  	}
  1006  	if source == "" {
  1007  		return fmt.Errorf("can't find any interface address to use")
  1008  	}
  1009  
  1010  	if err := natTable(ipv6, "-A", "POSTROUTING", "-p", "tcp", "-j", "SNAT", "--to-source", source); err != nil {
  1011  		return err
  1012  	}
  1013  	return connectTCP(ctx, ip, acceptPort, ipv6)
  1014  }
  1015  
  1016  // LocalAction implements TestCase.LocalAction.
  1017  func (*NATPostSNATTCP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
  1018  	remote, err := listenTCPFrom(ctx, acceptPort, ipv6)
  1019  	if err != nil {
  1020  		return err
  1021  	}
  1022  	HostStr, portStr, err := net.SplitHostPort(remote.String())
  1023  	if err != nil {
  1024  		return err
  1025  	}
  1026  	if got, want := HostStr, ip.String(); got != want {
  1027  		return fmt.Errorf("got remote address = %s, want = %s", got, want)
  1028  	}
  1029  	port, err := strconv.ParseInt(portStr, 10, 0)
  1030  	if err != nil {
  1031  		return err
  1032  	}
  1033  	if got, want := int(port), snatPort; got != want {
  1034  		return fmt.Errorf("got remote port = %d, want = %d", got, want)
  1035  	}
  1036  	return nil
  1037  }