2020-01-10 17:05:25 +00:00
|
|
|
// Copyright 2020 The gVisor Authors.
|
2020-01-09 23:38:28 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package iptables
|
|
|
|
|
|
|
|
import (
|
2020-07-24 22:54:16 +00:00
|
|
|
"context"
|
2020-02-15 01:19:32 +00:00
|
|
|
"errors"
|
2020-01-10 17:05:25 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
2021-04-10 04:09:47 +00:00
|
|
|
"strconv"
|
2021-01-19 20:10:01 +00:00
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
"golang.org/x/sys/unix"
|
2021-01-19 20:10:01 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/binary"
|
2021-03-29 20:28:32 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/hostarch"
|
2020-01-09 23:38:28 +00:00
|
|
|
)
|
|
|
|
|
2020-06-12 03:33:56 +00:00
|
|
|
const redirectPort = 42
|
2020-01-09 23:38:28 +00:00
|
|
|
|
|
|
|
func init() {
|
2021-02-11 22:37:37 +00:00
|
|
|
RegisterTestCase(&NATPreRedirectUDPPort{})
|
|
|
|
RegisterTestCase(&NATPreRedirectTCPPort{})
|
|
|
|
RegisterTestCase(&NATPreRedirectTCPOutgoing{})
|
|
|
|
RegisterTestCase(&NATOutRedirectTCPIncoming{})
|
|
|
|
RegisterTestCase(&NATOutRedirectUDPPort{})
|
|
|
|
RegisterTestCase(&NATOutRedirectTCPPort{})
|
|
|
|
RegisterTestCase(&NATDropUDP{})
|
|
|
|
RegisterTestCase(&NATAcceptAll{})
|
|
|
|
RegisterTestCase(&NATPreRedirectIP{})
|
|
|
|
RegisterTestCase(&NATPreDontRedirectIP{})
|
|
|
|
RegisterTestCase(&NATPreRedirectInvert{})
|
|
|
|
RegisterTestCase(&NATOutRedirectIP{})
|
|
|
|
RegisterTestCase(&NATOutDontRedirectIP{})
|
|
|
|
RegisterTestCase(&NATOutRedirectInvert{})
|
|
|
|
RegisterTestCase(&NATRedirectRequiresProtocol{})
|
|
|
|
RegisterTestCase(&NATLoopbackSkipsPrerouting{})
|
|
|
|
RegisterTestCase(&NATPreOriginalDst{})
|
|
|
|
RegisterTestCase(&NATOutOriginalDst{})
|
|
|
|
RegisterTestCase(&NATPreRECVORIGDSTADDR{})
|
|
|
|
RegisterTestCase(&NATOutRECVORIGDSTADDR{})
|
2021-04-10 04:09:47 +00:00
|
|
|
RegisterTestCase(&NATPostSNATUDP{})
|
|
|
|
RegisterTestCase(&NATPostSNATTCP{})
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// NATPreRedirectUDPPort tests that packets are redirected to different port.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreRedirectUDPPort struct{ containerCase }
|
2020-01-09 23:38:28 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRedirectUDPPort)(nil)
|
|
|
|
|
2020-01-09 23:38:28 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectUDPPort) Name() string {
|
2020-03-27 19:18:45 +00:00
|
|
|
return "NATPreRedirectUDPPort"
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
|
2020-01-09 23:38:28 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-17 17:59:54 +00:00
|
|
|
if err := listenUDP(ctx, redirectPort, ipv6); err != nil {
|
2020-01-10 17:05:25 +00:00
|
|
|
return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", redirectPort, err)
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
2020-02-18 19:30:42 +00:00
|
|
|
|
2020-01-09 23:38:28 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// NATPreRedirectTCPPort tests that connections are redirected on specified ports.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreRedirectTCPPort struct{ baseCase }
|
2020-02-18 19:30:42 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRedirectTCPPort)(nil)
|
|
|
|
|
2020-02-18 19:30:42 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPPort) Name() string {
|
2020-03-27 19:18:45 +00:00
|
|
|
return "NATPreRedirectTCPPort"
|
2020-02-18 19:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
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 {
|
2020-02-18 19:30:42 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Listen for TCP packets on redirect port.
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenTCP(ctx, acceptPort, ipv6)
|
2020-02-18 19:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return connectTCP(ctx, ip, dropPort, ipv6)
|
2020-02-18 19:30:42 +00:00
|
|
|
}
|
|
|
|
|
2020-06-05 18:22:44 +00:00
|
|
|
// NATPreRedirectTCPOutgoing verifies that outgoing TCP connections aren't
|
|
|
|
// affected by PREROUTING connection tracking.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreRedirectTCPOutgoing struct{ baseCase }
|
2020-06-05 18:22:44 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRedirectTCPOutgoing)(nil)
|
|
|
|
|
2020-06-05 18:22:44 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPOutgoing) Name() string {
|
2020-06-05 18:22:44 +00:00
|
|
|
return "NATPreRedirectTCPOutgoing"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPOutgoing) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-05 18:22:44 +00:00
|
|
|
// Redirect all incoming TCP traffic to a closed port.
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
|
2020-06-05 18:22:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establish a connection to the host process.
|
2021-02-17 17:59:54 +00:00
|
|
|
return connectTCP(ctx, ip, acceptPort, ipv6)
|
2020-06-05 18:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectTCPOutgoing) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenTCP(ctx, acceptPort, ipv6)
|
2020-06-05 18:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATOutRedirectTCPIncoming verifies that incoming TCP connections aren't
|
|
|
|
// affected by OUTPUT connection tracking.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutRedirectTCPIncoming struct{ baseCase }
|
2020-06-05 18:22:44 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRedirectTCPIncoming)(nil)
|
|
|
|
|
2020-06-05 18:22:44 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPIncoming) Name() string {
|
2020-06-05 18:22:44 +00:00
|
|
|
return "NATOutRedirectTCPIncoming"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPIncoming) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-05 18:22:44 +00:00
|
|
|
// Redirect all outgoing TCP traffic to a closed port.
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
|
2020-06-05 18:22:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establish a connection to the host process.
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenTCP(ctx, acceptPort, ipv6)
|
2020-06-05 18:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPIncoming) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return connectTCP(ctx, ip, acceptPort, ipv6)
|
2020-06-05 18:22:44 +00:00
|
|
|
}
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// NATOutRedirectUDPPort tests that packets are redirected to different port.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutRedirectUDPPort struct{ containerCase }
|
2020-03-27 19:18:45 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRedirectUDPPort)(nil)
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectUDPPort) Name() string {
|
2020-03-27 19:18:45 +00:00
|
|
|
return "NATOutRedirectUDPPort"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectUDPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-07-24 22:54:16 +00:00
|
|
|
return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)), "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort))
|
2020-03-27 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectUDPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-03-27 19:18:45 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// NATDropUDP tests that packets are not received in ports other than redirect
|
|
|
|
// port.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATDropUDP struct{ containerCase }
|
2020-01-09 23:38:28 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATDropUDP)(nil)
|
|
|
|
|
2020-01-09 23:38:28 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATDropUDP) Name() string {
|
2020-01-10 17:05:25 +00:00
|
|
|
return "NATDropUDP"
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATDropUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
|
2020-01-09 23:38:28 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-24 22:54:16 +00:00
|
|
|
timedCtx, cancel := context.WithTimeout(ctx, NegativeTimeout)
|
|
|
|
defer cancel()
|
2021-02-17 17:59:54 +00:00
|
|
|
if err := listenUDP(timedCtx, acceptPort, ipv6); err == nil {
|
2020-01-09 23:38:28 +00:00
|
|
|
return fmt.Errorf("packets on port %d should have been redirected to port %d", acceptPort, redirectPort)
|
2020-07-24 22:54:16 +00:00
|
|
|
} else if !errors.Is(err, context.DeadlineExceeded) {
|
|
|
|
return fmt.Errorf("error reading: %v", err)
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATDropUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2020-01-09 23:38:28 +00:00
|
|
|
}
|
2020-02-18 19:30:42 +00:00
|
|
|
|
|
|
|
// NATAcceptAll tests that all UDP packets are accepted.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATAcceptAll struct{ containerCase }
|
2020-02-18 19:30:42 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATAcceptAll)(nil)
|
|
|
|
|
2020-02-18 19:30:42 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATAcceptAll) Name() string {
|
2020-02-18 19:30:42 +00:00
|
|
|
return "NATAcceptAll"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATAcceptAll) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "ACCEPT"); err != nil {
|
2020-02-18 19:30:42 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-17 17:59:54 +00:00
|
|
|
if err := listenUDP(ctx, acceptPort, ipv6); err != nil {
|
2020-02-18 19:30:42 +00:00
|
|
|
return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", acceptPort, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATAcceptAll) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2020-02-18 19:30:42 +00:00
|
|
|
}
|
2020-03-16 18:13:14 +00:00
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// NATOutRedirectIP uses iptables to select packets based on destination IP and
|
|
|
|
// redirects them.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutRedirectIP struct{ baseCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRedirectIP)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectIP) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATOutRedirectIP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-02-15 01:19:32 +00:00
|
|
|
// Redirect OUTPUT packets to a listening localhost port.
|
2020-07-24 22:54:16 +00:00
|
|
|
return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)),
|
2020-06-15 05:40:46 +00:00
|
|
|
"-A", "OUTPUT",
|
|
|
|
"-d", nowhereIP(ipv6),
|
|
|
|
"-p", "udp",
|
|
|
|
"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-02-15 01:19:32 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NATOutDontRedirectIP tests that iptables matching with "-d" does not match
|
|
|
|
// packets it shouldn't.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutDontRedirectIP struct{ localCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutDontRedirectIP)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutDontRedirectIP) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATOutDontRedirectIP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutDontRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "OUTPUT", "-d", localIP(ipv6), "-p", "udp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
|
2020-02-15 01:19:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenUDP(ctx, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATOutRedirectInvert tests that iptables can match with "! -d".
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutRedirectInvert struct{ baseCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRedirectInvert)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectInvert) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATOutRedirectInvert"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectInvert) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-02-15 01:19:32 +00:00
|
|
|
// Redirect OUTPUT packets to a listening localhost port.
|
2020-06-15 05:40:46 +00:00
|
|
|
dest := "192.0.2.2"
|
|
|
|
if ipv6 {
|
|
|
|
dest = "2001:db8::2"
|
|
|
|
}
|
2020-07-24 22:54:16 +00:00
|
|
|
return loopbackTest(ctx, ipv6, net.ParseIP(nowhereIP(ipv6)),
|
2020-06-15 05:40:46 +00:00
|
|
|
"-A", "OUTPUT",
|
|
|
|
"!", "-d", dest,
|
|
|
|
"-p", "udp",
|
|
|
|
"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort))
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectInvert) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-02-15 01:19:32 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NATPreRedirectIP tests that we can use iptables to select packets based on
|
|
|
|
// destination IP and redirect them.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreRedirectIP struct{ containerCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRedirectIP)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectIP) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATPreRedirectIP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
addrs, err := localAddrs(ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var rules [][]string
|
|
|
|
for _, addr := range addrs {
|
|
|
|
rules = append(rules, []string{"-A", "PREROUTING", "-p", "udp", "-d", addr, "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)})
|
|
|
|
}
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTableRules(ipv6, rules); err != nil {
|
2020-02-15 01:19:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenUDP(ctx, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, dropPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATPreDontRedirectIP tests that iptables matching with "-d" does not match
|
|
|
|
// packets it shouldn't.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreDontRedirectIP struct{ containerCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreDontRedirectIP)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreDontRedirectIP) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATPreDontRedirectIP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreDontRedirectIP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", dropPort)); err != nil {
|
2020-02-15 01:19:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenUDP(ctx, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreDontRedirectIP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATPreRedirectInvert tests that iptables can match with "! -d".
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreRedirectInvert struct{ containerCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRedirectInvert)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectInvert) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATPreRedirectInvert"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectInvert) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "!", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err != nil {
|
2020-02-15 01:19:32 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-02-17 17:59:54 +00:00
|
|
|
return listenUDP(ctx, acceptPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRedirectInvert) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, dropPort, ipv6)
|
2020-02-15 01:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATRedirectRequiresProtocol tests that use of the --to-ports flag requires a
|
|
|
|
// protocol to be specified with -p.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATRedirectRequiresProtocol struct{ baseCase }
|
2020-02-15 01:19:32 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATRedirectRequiresProtocol)(nil)
|
|
|
|
|
2020-02-15 01:19:32 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATRedirectRequiresProtocol) Name() string {
|
2020-02-15 01:19:32 +00:00
|
|
|
return "NATRedirectRequiresProtocol"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATRedirectRequiresProtocol) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-d", localIP(ipv6), "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", acceptPort)); err == nil {
|
2020-02-15 01:19:32 +00:00
|
|
|
return errors.New("expected an error using REDIRECT --to-ports without a protocol")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATRedirectRequiresProtocol) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-02-15 01:19:32 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// NATOutRedirectTCPPort tests that connections are redirected on specified ports.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutRedirectTCPPort struct{ baseCase }
|
2020-03-27 19:18:45 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRedirectTCPPort)(nil)
|
|
|
|
|
2020-03-27 19:18:45 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPPort) Name() string {
|
2020-03-27 19:18:45 +00:00
|
|
|
return "NATOutRedirectTCPPort"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPPort) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
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 {
|
2020-03-27 19:18:45 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
localAddr := net.TCPAddr{
|
2020-06-15 05:40:46 +00:00
|
|
|
IP: net.ParseIP(localIP(ipv6)),
|
2020-03-27 19:18:45 +00:00
|
|
|
Port: acceptPort,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Starts listening on port.
|
|
|
|
lConn, err := net.ListenTCP("tcp", &localAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer lConn.Close()
|
|
|
|
|
|
|
|
// Accept connections on port.
|
2021-02-17 17:59:54 +00:00
|
|
|
if err := connectTCP(ctx, ip, dropPort, ipv6); err != nil {
|
2020-03-27 19:18:45 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := lConn.AcceptTCP()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRedirectTCPPort) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-03-27 19:18:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-06-04 02:57:39 +00:00
|
|
|
|
|
|
|
// NATLoopbackSkipsPrerouting tests that packets sent via loopback aren't
|
|
|
|
// affected by PREROUTING rules.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATLoopbackSkipsPrerouting struct{ baseCase }
|
2020-06-04 02:57:39 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATLoopbackSkipsPrerouting)(nil)
|
|
|
|
|
2020-06-04 02:57:39 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATLoopbackSkipsPrerouting) Name() string {
|
2020-06-04 02:57:39 +00:00
|
|
|
return "NATLoopbackSkipsPrerouting"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATLoopbackSkipsPrerouting) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-04 02:57:39 +00:00
|
|
|
// Redirect anything sent to localhost to an unused port.
|
2021-04-10 04:09:47 +00:00
|
|
|
var dest net.IP
|
|
|
|
if ipv6 {
|
|
|
|
dest = net.IPv6loopback
|
|
|
|
} else {
|
|
|
|
dest = net.IPv4(127, 0, 0, 1)
|
|
|
|
}
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", dropPort)); err != nil {
|
2020-06-04 02:57:39 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Establish a connection via localhost. If the PREROUTING rule did apply to
|
|
|
|
// loopback traffic, the connection would fail.
|
|
|
|
sendCh := make(chan error)
|
|
|
|
go func() {
|
2021-02-17 17:59:54 +00:00
|
|
|
sendCh <- connectTCP(ctx, dest, acceptPort, ipv6)
|
2020-06-04 02:57:39 +00:00
|
|
|
}()
|
|
|
|
|
2021-02-17 17:59:54 +00:00
|
|
|
if err := listenTCP(ctx, acceptPort, ipv6); err != nil {
|
2020-06-04 02:57:39 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return <-sendCh
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATLoopbackSkipsPrerouting) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-04 02:57:39 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-12 03:33:56 +00:00
|
|
|
// NATPreOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
|
|
|
|
// of PREROUTING NATted packets.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATPreOriginalDst struct{ baseCase }
|
2020-06-12 03:33:56 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreOriginalDst)(nil)
|
|
|
|
|
2020-06-12 03:33:56 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreOriginalDst) Name() string {
|
2020-06-12 03:33:56 +00:00
|
|
|
return "NATPreOriginalDst"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreOriginalDst) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-12 03:33:56 +00:00
|
|
|
// Redirect incoming TCP connections to acceptPort.
|
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING",
|
|
|
|
"-p", "tcp",
|
|
|
|
"--destination-port", fmt.Sprintf("%d", dropPort),
|
|
|
|
"-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs, err := getInterfaceAddrs(ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-24 22:54:16 +00:00
|
|
|
return listenForRedirectedConn(ctx, ipv6, addrs)
|
2020-06-12 03:33:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return connectTCP(ctx, ip, dropPort, ipv6)
|
2020-06-12 03:33:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATOutOriginalDst tests that SO_ORIGINAL_DST returns the pre-NAT destination
|
|
|
|
// of OUTBOUND NATted packets.
|
2020-07-24 22:54:16 +00:00
|
|
|
type NATOutOriginalDst struct{ baseCase }
|
2020-06-12 03:33:56 +00:00
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutOriginalDst)(nil)
|
|
|
|
|
2020-06-12 03:33:56 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutOriginalDst) Name() string {
|
2020-06-12 03:33:56 +00:00
|
|
|
return "NATOutOriginalDst"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutOriginalDst) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-12 03:33:56 +00:00
|
|
|
// Redirect incoming TCP connections to acceptPort.
|
|
|
|
if err := natTable(ipv6, "-A", "OUTPUT", "-p", "tcp", "-j", "REDIRECT", "--to-port", fmt.Sprintf("%d", acceptPort)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
connCh := make(chan error)
|
|
|
|
go func() {
|
2021-02-17 17:59:54 +00:00
|
|
|
connCh <- connectTCP(ctx, ip, dropPort, ipv6)
|
2020-06-12 03:33:56 +00:00
|
|
|
}()
|
|
|
|
|
2020-07-24 22:54:16 +00:00
|
|
|
if err := listenForRedirectedConn(ctx, ipv6, []net.IP{ip}); err != nil {
|
2020-06-12 03:33:56 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return <-connCh
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutOriginalDst) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2020-06-12 03:33:56 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-24 22:54:16 +00:00
|
|
|
func listenForRedirectedConn(ctx context.Context, ipv6 bool, originalDsts []net.IP) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
// The net package doesn't give guaranteed access to the connection's
|
2020-06-12 03:33:56 +00:00
|
|
|
// underlying FD, and thus we cannot call getsockopt. We have to use
|
2021-01-19 20:10:01 +00:00
|
|
|
// traditional syscalls.
|
2020-06-12 03:33:56 +00:00
|
|
|
|
|
|
|
// Create the listening socket, bind, listen, and accept.
|
2021-03-06 17:52:23 +00:00
|
|
|
family := unix.AF_INET
|
2020-06-12 03:33:56 +00:00
|
|
|
if ipv6 {
|
2021-03-06 17:52:23 +00:00
|
|
|
family = unix.AF_INET6
|
2020-06-12 03:33:56 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
sockfd, err := unix.Socket(family, unix.SOCK_STREAM, 0)
|
2020-06-12 03:33:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
defer unix.Close(sockfd)
|
2020-06-12 03:33:56 +00:00
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
var bindAddr unix.Sockaddr
|
2020-06-12 03:33:56 +00:00
|
|
|
if ipv6 {
|
2021-03-06 17:52:23 +00:00
|
|
|
bindAddr = &unix.SockaddrInet6{
|
2020-06-12 03:33:56 +00:00
|
|
|
Port: acceptPort,
|
|
|
|
Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
|
|
|
|
}
|
|
|
|
} else {
|
2021-03-06 17:52:23 +00:00
|
|
|
bindAddr = &unix.SockaddrInet4{
|
2020-06-12 03:33:56 +00:00
|
|
|
Port: acceptPort,
|
|
|
|
Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
|
|
|
|
}
|
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
if err := unix.Bind(sockfd, bindAddr); err != nil {
|
2020-06-12 03:33:56 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
if err := unix.Listen(sockfd, 1); err != nil {
|
2020-06-12 03:33:56 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-24 22:54:16 +00:00
|
|
|
// Block on accept() in another goroutine.
|
|
|
|
connCh := make(chan int)
|
|
|
|
errCh := make(chan error)
|
|
|
|
go func() {
|
2020-10-23 22:44:22 +00:00
|
|
|
for {
|
2021-03-06 17:52:23 +00:00
|
|
|
connFD, _, err := unix.Accept(sockfd)
|
|
|
|
if errors.Is(err, unix.EINTR) {
|
2020-10-23 22:44:22 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
errCh <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
connCh <- connFD
|
|
|
|
return
|
2020-07-24 22:54:16 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Wait for accept() to return or for the context to finish.
|
|
|
|
var connFD int
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case err := <-errCh:
|
2020-06-12 03:33:56 +00:00
|
|
|
return err
|
2020-07-24 22:54:16 +00:00
|
|
|
case connFD = <-connCh:
|
2020-06-12 03:33:56 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
defer unix.Close(connFD)
|
2020-06-12 03:33:56 +00:00
|
|
|
|
|
|
|
// Verify that, despite listening on acceptPort, SO_ORIGINAL_DST
|
|
|
|
// indicates the packet was sent to originalDst:dropPort.
|
|
|
|
if ipv6 {
|
2020-07-24 22:54:16 +00:00
|
|
|
got, err := originalDestination6(connFD)
|
2020-06-12 03:33:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-19 20:10:01 +00:00
|
|
|
return addrMatches6(got, originalDsts, dropPort)
|
2021-01-12 20:36:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
got, err := originalDestination4(connFD)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-19 20:10:01 +00:00
|
|
|
return addrMatches4(got, originalDsts, dropPort)
|
2020-06-12 03:33:56 +00:00
|
|
|
}
|
|
|
|
|
2020-06-04 02:57:39 +00:00
|
|
|
// loopbackTests runs an iptables rule and ensures that packets sent to
|
|
|
|
// dest:dropPort are received by localhost:acceptPort.
|
2020-07-24 22:54:16 +00:00
|
|
|
func loopbackTest(ctx context.Context, ipv6 bool, dest net.IP, args ...string) error {
|
2020-06-15 05:40:46 +00:00
|
|
|
if err := natTable(ipv6, args...); err != nil {
|
2020-06-04 02:57:39 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-07-24 22:54:16 +00:00
|
|
|
sendCh := make(chan error, 1)
|
|
|
|
listenCh := make(chan error, 1)
|
2020-06-04 02:57:39 +00:00
|
|
|
go func() {
|
2021-02-17 17:59:54 +00:00
|
|
|
sendCh <- sendUDPLoop(ctx, dest, dropPort, ipv6)
|
2020-06-04 02:57:39 +00:00
|
|
|
}()
|
|
|
|
go func() {
|
2021-02-17 17:59:54 +00:00
|
|
|
listenCh <- listenUDP(ctx, acceptPort, ipv6)
|
2020-06-04 02:57:39 +00:00
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case err := <-listenCh:
|
2020-07-24 22:54:16 +00:00
|
|
|
return err
|
|
|
|
case err := <-sendCh:
|
|
|
|
return err
|
2020-06-04 02:57:39 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-19 20:10:01 +00:00
|
|
|
|
|
|
|
// NATPreRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
|
|
|
|
// address on the PREROUTING chain.
|
|
|
|
type NATPreRECVORIGDSTADDR struct{ containerCase }
|
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATPreRECVORIGDSTADDR)(nil)
|
|
|
|
|
2021-01-19 20:10:01 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRECVORIGDSTADDR) Name() string {
|
2021-01-19 20:10:01 +00:00
|
|
|
return "NATPreRECVORIGDSTADDR"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRECVORIGDSTADDR) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
if err := natTable(ipv6, "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := recvWithRECVORIGDSTADDR(ctx, ipv6, nil, redirectPort); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATPreRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-02-17 17:59:54 +00:00
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NATOutRECVORIGDSTADDR tests that IP{V6}_RECVORIGDSTADDR gets the post-NAT
|
|
|
|
// address on the OUTPUT chain.
|
|
|
|
type NATOutRECVORIGDSTADDR struct{ containerCase }
|
|
|
|
|
2021-02-11 22:37:37 +00:00
|
|
|
var _ TestCase = (*NATOutRECVORIGDSTADDR)(nil)
|
|
|
|
|
2021-01-19 20:10:01 +00:00
|
|
|
// Name implements TestCase.Name.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRECVORIGDSTADDR) Name() string {
|
2021-01-19 20:10:01 +00:00
|
|
|
return "NATOutRECVORIGDSTADDR"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRECVORIGDSTADDR) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
if err := natTable(ipv6, "-A", "OUTPUT", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sendCh := make(chan error)
|
|
|
|
go func() {
|
|
|
|
// Packets will be sent to a non-container IP and redirected
|
|
|
|
// back to the container.
|
2021-02-17 17:59:54 +00:00
|
|
|
sendCh <- sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
2021-01-19 20:10:01 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
expectedIP := &net.IP{127, 0, 0, 1}
|
|
|
|
if ipv6 {
|
|
|
|
expectedIP = &net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
|
|
|
}
|
|
|
|
if err := recvWithRECVORIGDSTADDR(ctx, ipv6, expectedIP, redirectPort); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case err := <-sendCh:
|
|
|
|
return err
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
2021-02-11 22:37:37 +00:00
|
|
|
func (*NATOutRECVORIGDSTADDR) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
// No-op.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func recvWithRECVORIGDSTADDR(ctx context.Context, ipv6 bool, expectedDst *net.IP, port uint16) error {
|
|
|
|
// The net package doesn't give guaranteed access to a connection's
|
|
|
|
// underlying FD, and thus we cannot call getsockopt. We have to use
|
|
|
|
// traditional syscalls for IP_RECVORIGDSTADDR.
|
|
|
|
|
|
|
|
// Create the listening socket.
|
|
|
|
var (
|
2021-03-06 17:52:23 +00:00
|
|
|
family = unix.AF_INET
|
|
|
|
level = unix.SOL_IP
|
|
|
|
option = unix.IP_RECVORIGDSTADDR
|
|
|
|
bindAddr unix.Sockaddr = &unix.SockaddrInet4{
|
2021-01-19 20:10:01 +00:00
|
|
|
Port: int(port),
|
|
|
|
Addr: [4]byte{0, 0, 0, 0}, // INADDR_ANY
|
|
|
|
}
|
|
|
|
)
|
|
|
|
if ipv6 {
|
2021-03-06 17:52:23 +00:00
|
|
|
family = unix.AF_INET6
|
|
|
|
level = unix.SOL_IPV6
|
2021-01-19 20:10:01 +00:00
|
|
|
option = 74 // IPV6_RECVORIGDSTADDR, which is missing from the syscall package.
|
2021-03-06 17:52:23 +00:00
|
|
|
bindAddr = &unix.SockaddrInet6{
|
2021-01-19 20:10:01 +00:00
|
|
|
Port: int(port),
|
|
|
|
Addr: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // in6addr_any
|
|
|
|
}
|
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
sockfd, err := unix.Socket(family, unix.SOCK_DGRAM, 0)
|
2021-01-19 20:10:01 +00:00
|
|
|
if err != nil {
|
2021-03-06 17:52:23 +00:00
|
|
|
return fmt.Errorf("failed Socket(%d, %d, 0): %w", family, unix.SOCK_DGRAM, err)
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
defer unix.Close(sockfd)
|
2021-01-19 20:10:01 +00:00
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
if err := unix.Bind(sockfd, bindAddr); err != nil {
|
2021-01-19 20:10:01 +00:00
|
|
|
return fmt.Errorf("failed Bind(%d, %+v): %v", sockfd, bindAddr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable IP_RECVORIGDSTADDR.
|
2021-03-06 17:52:23 +00:00
|
|
|
if err := unix.SetsockoptInt(sockfd, level, option, 1); err != nil {
|
2021-01-19 20:10:01 +00:00
|
|
|
return fmt.Errorf("failed SetsockoptByte(%d, %d, %d, 1): %v", sockfd, level, option, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
addrCh := make(chan interface{})
|
|
|
|
errCh := make(chan error)
|
|
|
|
go func() {
|
|
|
|
var addr interface{}
|
|
|
|
var err error
|
|
|
|
if ipv6 {
|
|
|
|
addr, err = recvOrigDstAddr6(sockfd)
|
|
|
|
} else {
|
|
|
|
addr, err = recvOrigDstAddr4(sockfd)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
errCh <- err
|
|
|
|
} else {
|
|
|
|
addrCh <- addr
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Wait to receive a packet.
|
|
|
|
var addr interface{}
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case err := <-errCh:
|
|
|
|
return err
|
|
|
|
case addr = <-addrCh:
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a list of local IPs to verify that the packet now appears to have
|
|
|
|
// been sent to us.
|
|
|
|
var localAddrs []net.IP
|
|
|
|
if expectedDst != nil {
|
|
|
|
localAddrs = []net.IP{*expectedDst}
|
|
|
|
} else {
|
|
|
|
localAddrs, err = getInterfaceAddrs(ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to get local interfaces: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the address has the post-NAT port and address.
|
|
|
|
if ipv6 {
|
2021-03-06 17:52:23 +00:00
|
|
|
return addrMatches6(addr.(unix.RawSockaddrInet6), localAddrs, redirectPort)
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
return addrMatches4(addr.(unix.RawSockaddrInet4), localAddrs, redirectPort)
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
func recvOrigDstAddr4(sockfd int) (unix.RawSockaddrInet4, error) {
|
|
|
|
buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet4)
|
2021-01-19 20:10:01 +00:00
|
|
|
if err != nil {
|
2021-03-06 17:52:23 +00:00
|
|
|
return unix.RawSockaddrInet4{}, err
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
var addr unix.RawSockaddrInet4
|
2021-03-29 20:28:32 +00:00
|
|
|
binary.Unmarshal(buf, hostarch.ByteOrder, &addr)
|
2021-01-19 20:10:01 +00:00
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
func recvOrigDstAddr6(sockfd int) (unix.RawSockaddrInet6, error) {
|
|
|
|
buf, err := recvOrigDstAddr(sockfd, unix.SOL_IP, unix.SizeofSockaddrInet6)
|
2021-01-19 20:10:01 +00:00
|
|
|
if err != nil {
|
2021-03-06 17:52:23 +00:00
|
|
|
return unix.RawSockaddrInet6{}, err
|
2021-01-19 20:10:01 +00:00
|
|
|
}
|
2021-03-06 17:52:23 +00:00
|
|
|
var addr unix.RawSockaddrInet6
|
2021-03-29 20:28:32 +00:00
|
|
|
binary.Unmarshal(buf, hostarch.ByteOrder, &addr)
|
2021-01-19 20:10:01 +00:00
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func recvOrigDstAddr(sockfd int, level uintptr, addrSize int) ([]byte, error) {
|
|
|
|
buf := make([]byte, 64)
|
2021-03-06 17:52:23 +00:00
|
|
|
oob := make([]byte, unix.CmsgSpace(addrSize))
|
2021-01-19 20:10:01 +00:00
|
|
|
for {
|
2021-03-06 17:52:23 +00:00
|
|
|
_, oobn, _, _, err := unix.Recvmsg(
|
2021-01-19 20:10:01 +00:00
|
|
|
sockfd,
|
|
|
|
buf, // Message buffer.
|
|
|
|
oob, // Out-of-band buffer.
|
|
|
|
0) // Flags.
|
2021-03-06 17:52:23 +00:00
|
|
|
if errors.Is(err, unix.EINTR) {
|
2021-01-19 20:10:01 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed when calling Recvmsg: %w", err)
|
|
|
|
}
|
|
|
|
oob = oob[:oobn]
|
|
|
|
|
|
|
|
// Parse out the control message.
|
2021-03-06 17:52:23 +00:00
|
|
|
msgs, err := unix.ParseSocketControlMessage(oob)
|
2021-01-19 20:10:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parse control message: %w", err)
|
|
|
|
}
|
|
|
|
return msgs[0].Data, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
func addrMatches4(got unix.RawSockaddrInet4, wantAddrs []net.IP, port uint16) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
for _, wantAddr := range wantAddrs {
|
2021-03-06 17:52:23 +00:00
|
|
|
want := unix.RawSockaddrInet4{
|
|
|
|
Family: unix.AF_INET,
|
2021-01-19 20:10:01 +00:00
|
|
|
Port: htons(port),
|
|
|
|
}
|
|
|
|
copy(want.Addr[:], wantAddr.To4())
|
|
|
|
if got == want {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
|
|
|
|
}
|
|
|
|
|
2021-03-06 17:52:23 +00:00
|
|
|
func addrMatches6(got unix.RawSockaddrInet6, wantAddrs []net.IP, port uint16) error {
|
2021-01-19 20:10:01 +00:00
|
|
|
for _, wantAddr := range wantAddrs {
|
2021-03-06 17:52:23 +00:00
|
|
|
want := unix.RawSockaddrInet6{
|
|
|
|
Family: unix.AF_INET6,
|
2021-01-19 20:10:01 +00:00
|
|
|
Port: htons(port),
|
|
|
|
}
|
|
|
|
copy(want.Addr[:], wantAddr.To16())
|
|
|
|
if got == want {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Errorf("got %+v, but wanted one of %+v (note: port numbers are in network byte order)", got, wantAddrs)
|
|
|
|
}
|
2021-04-10 04:09:47 +00:00
|
|
|
|
|
|
|
const (
|
|
|
|
snatAddrV4 = "194.236.50.155"
|
|
|
|
snatAddrV6 = "2a0a::1"
|
|
|
|
snatPort = 43
|
|
|
|
)
|
|
|
|
|
|
|
|
// NATPostSNATUDP tests that the source port/IP in the packets are modified as expected.
|
|
|
|
type NATPostSNATUDP struct{ localCase }
|
|
|
|
|
|
|
|
var _ TestCase = (*NATPostSNATUDP)(nil)
|
|
|
|
|
|
|
|
// Name implements TestCase.Name.
|
|
|
|
func (*NATPostSNATUDP) Name() string {
|
|
|
|
return "NATPostSNATUDP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
|
|
|
func (*NATPostSNATUDP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
|
|
|
var source string
|
|
|
|
if ipv6 {
|
|
|
|
source = fmt.Sprintf("[%s]:%d", snatAddrV6, snatPort)
|
|
|
|
} else {
|
|
|
|
source = fmt.Sprintf("%s:%d", snatAddrV4, snatPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := natTable(ipv6, "-A", "POSTROUTING", "-p", "udp", "-j", "SNAT", "--to-source", source); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return sendUDPLoop(ctx, ip, acceptPort, ipv6)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
|
|
|
func (*NATPostSNATUDP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
|
|
|
remote, err := listenUDPFrom(ctx, acceptPort, ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var snatAddr string
|
|
|
|
if ipv6 {
|
|
|
|
snatAddr = snatAddrV6
|
|
|
|
} else {
|
|
|
|
snatAddr = snatAddrV4
|
|
|
|
}
|
|
|
|
if got, want := remote.IP, net.ParseIP(snatAddr); !got.Equal(want) {
|
|
|
|
return fmt.Errorf("got remote address = %s, want = %s", got, want)
|
|
|
|
}
|
|
|
|
if got, want := remote.Port, snatPort; got != want {
|
|
|
|
return fmt.Errorf("got remote port = %d, want = %d", got, want)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NATPostSNATTCP tests that the source port/IP in the packets are modified as
|
|
|
|
// expected.
|
|
|
|
type NATPostSNATTCP struct{ localCase }
|
|
|
|
|
|
|
|
var _ TestCase = (*NATPostSNATTCP)(nil)
|
|
|
|
|
|
|
|
// Name implements TestCase.Name.
|
|
|
|
func (*NATPostSNATTCP) Name() string {
|
|
|
|
return "NATPostSNATTCP"
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContainerAction implements TestCase.ContainerAction.
|
|
|
|
func (*NATPostSNATTCP) ContainerAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
|
|
|
addrs, err := getInterfaceAddrs(ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var source string
|
|
|
|
for _, addr := range addrs {
|
|
|
|
if addr.To4() != nil {
|
|
|
|
if !ipv6 {
|
|
|
|
source = fmt.Sprintf("%s:%d", addr, snatPort)
|
|
|
|
}
|
|
|
|
} else if ipv6 && addr.IsGlobalUnicast() {
|
|
|
|
source = fmt.Sprintf("[%s]:%d", addr, snatPort)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if source == "" {
|
|
|
|
return fmt.Errorf("can't find any interface address to use")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := natTable(ipv6, "-A", "POSTROUTING", "-p", "tcp", "-j", "SNAT", "--to-source", source); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return connectTCP(ctx, ip, acceptPort, ipv6)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalAction implements TestCase.LocalAction.
|
|
|
|
func (*NATPostSNATTCP) LocalAction(ctx context.Context, ip net.IP, ipv6 bool) error {
|
|
|
|
remote, err := listenTCPFrom(ctx, acceptPort, ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
HostStr, portStr, err := net.SplitHostPort(remote.String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if got, want := HostStr, ip.String(); got != want {
|
|
|
|
return fmt.Errorf("got remote address = %s, want = %s", got, want)
|
|
|
|
}
|
|
|
|
port, err := strconv.ParseInt(portStr, 10, 0)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if got, want := int(port), snatPort; got != want {
|
|
|
|
return fmt.Errorf("got remote port = %d, want = %d", got, want)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|