Skip to content

Commit

Permalink
fix port forwarding with ipv6.disable=1
Browse files Browse the repository at this point in the history
Make `docker run -p 80:80` functional again on environments with kernel boot parameter `ipv6.disable=1`.

Fix moby/moby issue 42288

Signed-off-by: Akihiro Suda <[email protected]>
  • Loading branch information
AkihiroSuda committed Apr 27, 2021
1 parent b350742 commit 7b9c290
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
31 changes: 31 additions & 0 deletions drivers/bridge/port_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"sync"

"github.com/docker/libnetwork/types"
"github.com/ishidawataru/sctp"
Expand Down Expand Up @@ -50,6 +51,13 @@ func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, cont
bs = append(bs, bIPv4)
}

// skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1`
// https://github.com/moby/moby/issues/42288
isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil
if !isV6Binding && !IsV6Listenable() {
continue
}

// Allocate IPv6 Port mappings
// If the container has no IPv6 address, allow proxying host IPv6 traffic to it
// by setting up the binding with the IPv4 interface if the userland proxy is enabled
Expand Down Expand Up @@ -211,3 +219,26 @@ func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {

return portmapper.Unmap(host)
}

var (
v6ListenableCached bool
v6ListenableOnce sync.Once
)

// IsV6Listenable returns true when `[::1]:0` is listenable.
// IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option.
func IsV6Listenable() bool {
v6ListenableOnce.Do(func() {
ln, err := net.Listen("tcp6", "[::1]:0")
if err != nil {
// When the kernel was booted with `ipv6.disable=1`,
// we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol"
// https://github.com/moby/moby/issues/42288
logrus.Debugf("port_mapping: v6Listenable=false (%v)", err)
} else {
v6ListenableCached = true
ln.Close()
}
})
return v6ListenableCached
}
7 changes: 6 additions & 1 deletion libnetwork_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/drivers/bridge"
"github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
Expand Down Expand Up @@ -199,7 +200,11 @@ func TestBridge(t *testing.T) {
if !ok {
t.Fatalf("Unexpected format for port mapping in endpoint operational data")
}
if len(pm) != 10 {
expectedLen := 10
if !bridge.IsV6Listenable() {
expectedLen = 5
}
if len(pm) != expectedLen {
t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm))
}
}
Expand Down

0 comments on commit 7b9c290

Please sign in to comment.