aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/rootlessport/main.go24
-rw-r--r--cmd/rootlessport/wsl.go37
-rw-r--r--cmd/rootlessport/wsl_test.go89
3 files changed, 145 insertions, 5 deletions
diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go
index e9ab8b076..5bd35a985 100644
--- a/cmd/rootlessport/main.go
+++ b/cmd/rootlessport/main.go
@@ -1,3 +1,6 @@
+//go:build linux
+// +build linux
+
package main
import (
@@ -307,11 +310,11 @@ func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP st
ChildPort: int(port.ContainerPort + i),
ChildIP: childIP,
}
- if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
- return err
- }
- if _, err := pm.AddPort(ctx, spec); err != nil {
- return err
+
+ for _, spec = range splitDualStackSpecIfWsl(spec) {
+ if err := validateAndAddPort(ctx, pm, spec); err != nil {
+ return err
+ }
}
}
}
@@ -319,6 +322,17 @@ func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP st
return nil
}
+func validateAndAddPort(ctx context.Context, pm rkport.Manager, spec rkport.Spec) error {
+ if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
+ return err
+ }
+ if _, err := pm.AddPort(ctx, spec); err != nil {
+ return err
+ }
+
+ return nil
+}
+
func child() error {
// load the config from the parent
var opaque map[string]string
diff --git a/cmd/rootlessport/wsl.go b/cmd/rootlessport/wsl.go
new file mode 100644
index 000000000..c1e67ba87
--- /dev/null
+++ b/cmd/rootlessport/wsl.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "net"
+ "strings"
+
+ "github.com/containers/common/pkg/machine"
+ rkport "github.com/rootless-containers/rootlesskit/pkg/port"
+)
+
+// WSL machines do not relay ipv4 traffic to dual-stack ports, simulate instead
+func splitDualStackSpecIfWsl(spec rkport.Spec) []rkport.Spec {
+ specs := []rkport.Spec{spec}
+ protocol := spec.Proto
+ if machine.MachineHostType() != machine.Wsl || strings.HasSuffix(protocol, "4") || strings.HasSuffix(protocol, "6") {
+ return specs
+ }
+
+ ip := net.ParseIP(spec.ParentIP)
+ splitLoopback := ip.IsLoopback() && ip.To4() == nil
+ // Map ::1 and 0.0.0.0/:: to ipv4 + ipv6 to simulate dual-stack
+ if ip.IsUnspecified() || splitLoopback {
+ specs = append(specs, spec)
+ specs[0].Proto = protocol + "4"
+ specs[1].Proto = protocol + "6"
+ if splitLoopback {
+ // Hacky, but we will only have one ipv4 loopback with WSL config
+ specs[0].ParentIP = "127.0.0.1"
+ }
+ if ip.IsUnspecified() {
+ specs[0].ParentIP = "0.0.0.0"
+ specs[1].ParentIP = "::"
+ }
+ }
+
+ return specs
+}
diff --git a/cmd/rootlessport/wsl_test.go b/cmd/rootlessport/wsl_test.go
new file mode 100644
index 000000000..83d7e3717
--- /dev/null
+++ b/cmd/rootlessport/wsl_test.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/containers/common/pkg/machine"
+ "github.com/rootless-containers/rootlesskit/pkg/port"
+ "github.com/stretchr/testify/assert"
+)
+
+type SpecData struct {
+ mach string
+ sourceProto string
+ sourceIP string
+ expectCount int
+ expectProto string
+ expectIP string
+ secondProto string
+ secondIP string
+}
+
+func TestDualStackSplit(t *testing.T) {
+ //nolint
+ const (
+ IP4_ALL = "0.0.0.0"
+ IP4__LO = "127.0.0.1"
+ IP6_ALL = "::"
+ IP6__LO = "::1"
+ TCP_ = "tcp"
+ TCP4 = "tcp4"
+ TCP6 = "tcp6"
+ WSL = "wsl"
+ ___ = ""
+ IP6_REG = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
+ IP4_REG = "10.0.0.1"
+ )
+
+ tests := []SpecData{
+ // Split cases
+ {WSL, TCP_, IP4_ALL, 2, TCP4, IP4_ALL, TCP6, IP6_ALL},
+ {WSL, TCP_, IP6_ALL, 2, TCP4, IP4_ALL, TCP6, IP6_ALL},
+ {WSL, TCP_, IP6__LO, 2, TCP4, IP4__LO, TCP6, IP6__LO},
+
+ // Non-Split
+ {WSL, TCP_, IP4__LO, 1, TCP_, IP4__LO, "", ""},
+ {WSL, TCP4, IP4_ALL, 1, TCP4, IP4_ALL, "", ""},
+ {WSL, TCP6, IP6__LO, 1, TCP6, IP6__LO, "", ""},
+ {WSL, TCP_, IP4_REG, 1, TCP_, IP4_REG, "", ""},
+ {WSL, TCP_, IP6_REG, 1, TCP_, IP6_REG, "", ""},
+ {___, TCP_, IP4_ALL, 1, TCP_, IP4_ALL, "", ""},
+ {___, TCP_, IP6_ALL, 1, TCP_, IP6_ALL, "", ""},
+ {___, TCP_, IP4__LO, 1, TCP_, IP4__LO, "", ""},
+ {___, TCP_, IP6__LO, 1, TCP_, IP6__LO, "", ""},
+ }
+
+ for _, data := range tests {
+ verifySplit(t, data)
+ }
+}
+
+func verifySplit(t *testing.T, data SpecData) {
+ machine := machine.GetMachineMarker()
+ oldEnable, oldType := machine.Enabled, machine.Type
+ machine.Enabled, machine.Type = len(data.mach) > 0, data.mach
+
+ source := port.Spec{
+ Proto: data.sourceProto,
+ ParentIP: data.sourceIP,
+ ParentPort: 100,
+ ChildIP: "1.1.1.1",
+ ChildPort: 200,
+ }
+ expect, second := source, source
+ specs := splitDualStackSpecIfWsl(source)
+
+ assert.Equal(t, data.expectCount, len(specs))
+
+ expect.Proto = data.expectProto
+ expect.ParentIP = data.expectIP
+ assert.Equal(t, expect, specs[0])
+
+ if data.expectCount > 1 {
+ second.Proto = data.secondProto
+ second.ParentIP = data.secondIP
+ assert.Equal(t, second, specs[1])
+ }
+
+ machine.Enabled, machine.Type = oldEnable, oldType
+}