Skip to content

Commit c5b25bf

Browse files
authored
Merge pull request moby#50225 from robmry/TestRoutedNonGateway
Add TestRoutedNonGateway
2 parents 3ff85c7 + 4ccbca1 commit c5b25bf

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

integration/networking/port_mapping_linux_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,140 @@ func TestDirectRoutingOpenPorts(t *testing.T) {
798798
}
799799
}
800800

801+
// TestRoutedNonGateway checks whether a published container port on an endpoint in a
802+
// gateway mode "routed" network is accessible when the routed network is not providing
803+
// the container's default gateway.
804+
func TestRoutedNonGateway(t *testing.T) {
805+
skip.If(t, testEnv.IsRootless())
806+
skip.If(t, networking.FirewalldRunning(), "Firewalld's IPv6_rpfilter=yes breaks IPv6 direct routing from L3Segment")
807+
808+
ctx := setupTest(t)
809+
d := daemon.New(t)
810+
d.StartWithBusybox(ctx, t)
811+
defer d.Stop(t)
812+
c := d.NewClientT(t)
813+
defer c.Close()
814+
815+
// Simulate the remote host.
816+
l3 := networking.NewL3Segment(t, "test-routed-open-ports",
817+
netip.MustParsePrefix("192.168.124.1/24"),
818+
netip.MustParsePrefix("fdc0:36dc:a4dd::1/64"))
819+
defer l3.Destroy(t)
820+
// "docker" is the host where dockerd is running.
821+
const dockerHostIPv4 = "192.168.124.2"
822+
const dockerHostIPv6 = "fdc0:36dc:a4dd::2"
823+
l3.AddHost(t, "docker", networking.CurrentNetns, "eth-test",
824+
netip.MustParsePrefix(dockerHostIPv4+"/24"),
825+
netip.MustParsePrefix(dockerHostIPv6+"/64"))
826+
// "remote" simulates the remote host.
827+
l3.AddHost(t, "remote", "test-remote-host", "eth0",
828+
netip.MustParsePrefix("192.168.124.3/24"),
829+
netip.MustParsePrefix("fdc0:36dc:a4dd::3/64"))
830+
// Add default routes from the "remote" Host to the "docker" Host.
831+
l3.Hosts["remote"].MustRun(t, "ip", "route", "add", "default", "via", "192.168.124.2")
832+
l3.Hosts["remote"].MustRun(t, "ip", "-6", "route", "add", "default", "via", "fdc0:36dc:a4dd::2")
833+
834+
// Create a dual-stack NAT'd network.
835+
const natNetName = "ds_nat"
836+
network.CreateNoError(ctx, t, c, natNetName,
837+
network.WithIPv6(),
838+
network.WithOption(bridge.BridgeName, natNetName),
839+
)
840+
defer network.RemoveNoError(ctx, t, c, natNetName)
841+
842+
// Create a dual-stack routed network.
843+
const routedNetName = "ds_routed"
844+
network.CreateNoError(ctx, t, c, routedNetName,
845+
network.WithIPv6(),
846+
network.WithOption(bridge.BridgeName, routedNetName),
847+
network.WithOption(bridge.IPv4GatewayMode, "routed"),
848+
network.WithOption(bridge.IPv6GatewayMode, "routed"),
849+
)
850+
defer network.RemoveNoError(ctx, t, c, routedNetName)
851+
852+
// Run a web server attached to both networks, and make sure the nat
853+
// network is selected as the gateway.
854+
ctrId := container.Run(ctx, t, c,
855+
container.WithCmd("httpd", "-f"),
856+
container.WithExposedPorts("80/tcp"),
857+
container.WithPortMap(nat.PortMap{"80/tcp": {{HostPort: "8080"}}}),
858+
container.WithNetworkMode(natNetName),
859+
container.WithNetworkMode(routedNetName),
860+
container.WithEndpointSettings(natNetName, &networktypes.EndpointSettings{GwPriority: 1}),
861+
container.WithEndpointSettings(routedNetName, &networktypes.EndpointSettings{GwPriority: 0}))
862+
defer container.Remove(ctx, t, c, ctrId, containertypes.RemoveOptions{Force: true})
863+
864+
testHttp := func(t *testing.T, addr, port, expOut string) {
865+
t.Helper()
866+
l3.Hosts["remote"].Do(t, func() {
867+
t.Helper()
868+
t.Parallel()
869+
u := "http://" + net.JoinHostPort(addr, port)
870+
res := icmd.RunCommand("curl", "--max-time", "3", "--show-error", "--silent", u)
871+
assert.Check(t, is.Contains(res.Combined(), expOut), "url:%s", u)
872+
})
873+
}
874+
875+
const (
876+
httpSuccess = "404 Not Found"
877+
httpFail = "Connection timed out"
878+
)
879+
880+
insp := container.Inspect(ctx, t, c, ctrId)
881+
testcases := []struct {
882+
name string
883+
addr string
884+
port string
885+
expHttp string
886+
}{
887+
{
888+
name: "nat/published/v4",
889+
addr: dockerHostIPv4,
890+
port: "8080",
891+
expHttp: httpSuccess,
892+
},
893+
{
894+
name: "nat/published/v6",
895+
addr: dockerHostIPv6,
896+
port: "8080",
897+
expHttp: httpSuccess,
898+
},
899+
{
900+
name: "nat/direct/v4",
901+
addr: insp.NetworkSettings.Networks[natNetName].IPAddress,
902+
port: "80",
903+
expHttp: httpFail,
904+
},
905+
{
906+
name: "nat/direct/v6",
907+
addr: insp.NetworkSettings.Networks[natNetName].GlobalIPv6Address,
908+
port: "80",
909+
expHttp: httpFail,
910+
},
911+
{
912+
name: "routed/direct/v4",
913+
addr: insp.NetworkSettings.Networks[routedNetName].IPAddress,
914+
port: "80",
915+
expHttp: httpFail,
916+
},
917+
{
918+
name: "routed/direct/v6",
919+
addr: insp.NetworkSettings.Networks[routedNetName].GlobalIPv6Address,
920+
port: "80",
921+
expHttp: httpFail,
922+
},
923+
}
924+
925+
// Wrap parallel tests, otherwise defer statements run before tests finish.
926+
t.Run("w", func(t *testing.T) {
927+
for _, tc := range testcases {
928+
t.Run(tc.name, func(t *testing.T) {
929+
testHttp(t, tc.addr, tc.port, tc.expHttp)
930+
})
931+
}
932+
})
933+
}
934+
801935
// TestAccessPublishedPortFromAnotherNetwork checks that a container can access
802936
// ports published on the host by a container attached to a different network
803937
// using both its gateway IP address, and the host IP address.

0 commit comments

Comments
 (0)