|
5 | 5 | "errors" |
6 | 6 | "fmt" |
7 | 7 | "net" |
| 8 | + "sync" |
8 | 9 |
|
9 | 10 | "github.com/docker/libnetwork/types" |
10 | 11 | "github.com/ishidawataru/sctp" |
@@ -50,6 +51,13 @@ func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, cont |
50 | 51 | bs = append(bs, bIPv4) |
51 | 52 | } |
52 | 53 |
|
| 54 | + // skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1` |
| 55 | + // https://github.com/moby/moby/issues/42288 |
| 56 | + isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil |
| 57 | + if !isV6Binding && !IsV6Listenable() { |
| 58 | + continue |
| 59 | + } |
| 60 | + |
53 | 61 | // Allocate IPv6 Port mappings |
54 | 62 | // If the container has no IPv6 address, allow proxying host IPv6 traffic to it |
55 | 63 | // by setting up the binding with the IPv4 interface if the userland proxy is enabled |
@@ -211,3 +219,26 @@ func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error { |
211 | 219 |
|
212 | 220 | return portmapper.Unmap(host) |
213 | 221 | } |
| 222 | + |
| 223 | +var ( |
| 224 | + v6ListenableCached bool |
| 225 | + v6ListenableOnce sync.Once |
| 226 | +) |
| 227 | + |
| 228 | +// IsV6Listenable returns true when `[::1]:0` is listenable. |
| 229 | +// IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option. |
| 230 | +func IsV6Listenable() bool { |
| 231 | + v6ListenableOnce.Do(func() { |
| 232 | + ln, err := net.Listen("tcp6", "[::1]:0") |
| 233 | + if err != nil { |
| 234 | + // When the kernel was booted with `ipv6.disable=1`, |
| 235 | + // we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol" |
| 236 | + // https://github.com/moby/moby/issues/42288 |
| 237 | + logrus.Debugf("port_mapping: v6Listenable=false (%v)", err) |
| 238 | + } else { |
| 239 | + v6ListenableCached = true |
| 240 | + ln.Close() |
| 241 | + } |
| 242 | + }) |
| 243 | + return v6ListenableCached |
| 244 | +} |
0 commit comments