Use simulated dual-stack binds when using WSL

Resolves a WSL problem where traffic from only one stack is relayed

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
Jason T. Greene 2022-04-25 15:55:42 -05:00
parent 5ac00a7287
commit 772ead2531
3 changed files with 145 additions and 5 deletions

View File

@ -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

37
cmd/rootlessport/wsl.go Normal file
View File

@ -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
}

View File

@ -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
}