-
Notifications
You must be signed in to change notification settings - Fork 880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix IPv6 Port Forwarding for the Bridge Driver #2604
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -11,71 +11,113 @@ import ( | |||||
"github.com/sirupsen/logrus" | ||||||
) | ||||||
|
||||||
var ( | ||||||
defaultBindingIP = net.IPv4(0, 0, 0, 0) | ||||||
defaultBindingIPV6 = net.ParseIP("::") | ||||||
) | ||||||
|
||||||
func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { | ||||||
if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { | ||||||
return nil, nil | ||||||
} | ||||||
|
||||||
defHostIP := defaultBindingIP | ||||||
defHostIP := net.IPv4zero // 0.0.0.0 | ||||||
if reqDefBindIP != nil { | ||||||
defHostIP = reqDefBindIP | ||||||
} | ||||||
|
||||||
// IPv4 port binding including user land proxy | ||||||
pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
var containerIPv6 net.IP | ||||||
if ep.addrv6 != nil { | ||||||
containerIPv6 = ep.addrv6.IP | ||||||
} | ||||||
|
||||||
// IPv6 port binding excluding user land proxy | ||||||
if n.driver.config.EnableIP6Tables && ep.addrv6 != nil { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Is there anything that prevents a creation of ip6tables rules in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If libnetwork/drivers/bridge/setup_ip_tables.go Line 184 in 5c6a95b
is skipped, pm.chain will be nil and will skip the iptables programminglibnetwork/portmapper/mapper_linux.go Line 42 in 5c6a95b
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh I see. That is for sure far cleaner than before. |
||||||
// TODO IPv6 custom default binding IP | ||||||
pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false) | ||||||
if err != nil { | ||||||
// ensure we clear the previous allocated IPv4 ports | ||||||
n.releasePortsInternal(pb) | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
pb = append(pb, pbv6...) | ||||||
pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
return pb, nil | ||||||
} | ||||||
|
||||||
func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { | ||||||
func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { | ||||||
bs := make([]types.PortBinding, 0, len(bindings)) | ||||||
for _, c := range bindings { | ||||||
b := c.GetCopy() | ||||||
if err := n.allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil { | ||||||
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message | ||||||
if cuErr := n.releasePortsInternal(bs); cuErr != nil { | ||||||
logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr) | ||||||
bIPv4 := c.GetCopy() | ||||||
bIPv6 := c.GetCopy() | ||||||
// Allocate IPv4 Port mappings | ||||||
if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok { | ||||||
if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil { | ||||||
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message | ||||||
if cuErr := n.releasePortsInternal(bs); cuErr != nil { | ||||||
logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr) | ||||||
} | ||||||
return nil, err | ||||||
} | ||||||
return nil, err | ||||||
bs = append(bs, bIPv4) | ||||||
} | ||||||
// Allocate IPv6 Port mappings | ||||||
if ok := n.validatePortBindingIPv6(&bIPv6, containerIPv6, defHostIP); ok { | ||||||
if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil { | ||||||
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message | ||||||
if cuErr := n.releasePortsInternal(bs); cuErr != nil { | ||||||
logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr) | ||||||
} | ||||||
return nil, err | ||||||
} | ||||||
bs = append(bs, bIPv6) | ||||||
} | ||||||
bs = append(bs, b) | ||||||
} | ||||||
return bs, nil | ||||||
} | ||||||
|
||||||
func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error { | ||||||
var ( | ||||||
host net.Addr | ||||||
err error | ||||||
) | ||||||
|
||||||
// Store the container interface address in the operational binding | ||||||
bnd.IP = containerIP | ||||||
|
||||||
// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true | ||||||
// if this is a valid IPv4 binding, else returns false | ||||||
func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool { | ||||||
//Return early if there is a valid Host IP, but its not a IPv6 address | ||||||
if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil { | ||||||
return false | ||||||
} | ||||||
// Adjust the host address in the operational binding | ||||||
if len(bnd.HostIP) == 0 { | ||||||
// Return early if the default binding address is an IPv6 address | ||||||
if defHostIP.To4() == nil { | ||||||
return false | ||||||
} | ||||||
bnd.HostIP = defHostIP | ||||||
} | ||||||
bnd.IP = containerIPv4 | ||||||
return true | ||||||
|
||||||
} | ||||||
|
||||||
// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true | ||||||
// if this is a valid IP6v binding, else returns false | ||||||
func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIPv6, defHostIP net.IP) bool { | ||||||
// Return early if there is no IPv6 container endpoint | ||||||
if containerIPv6 == nil { | ||||||
return false | ||||||
} | ||||||
// Return early if there is a valid Host IP, which is a IPv4 address | ||||||
if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil { | ||||||
return false | ||||||
} | ||||||
|
||||||
// Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0 | ||||||
if len(bnd.HostIP) == 0 { | ||||||
if defHostIP.Equal(net.IPv4zero) { | ||||||
bnd.HostIP = net.IPv6zero | ||||||
// If the default binding IP is an IPv6 address, use it | ||||||
} else if defHostIP.To4() == nil { | ||||||
bnd.HostIP = defHostIP | ||||||
// Return false if default binding ip is an IPv4 address | ||||||
} else { | ||||||
return false | ||||||
} | ||||||
} | ||||||
bnd.IP = containerIPv6 | ||||||
return true | ||||||
|
||||||
} | ||||||
|
||||||
func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error { | ||||||
var ( | ||||||
host net.Addr | ||||||
err error | ||||||
) | ||||||
|
||||||
// Adjust HostPortEnd if this is not a range. | ||||||
if bnd.HostPortEnd == 0 { | ||||||
|
@@ -90,7 +132,7 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos | |||||
|
||||||
portmapper := n.portMapper | ||||||
|
||||||
if containerIP.To4() == nil { | ||||||
if bnd.IP.To4() == nil { | ||||||
portmapper = n.portMapperV6 | ||||||
} | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@arkodg curious: would this be an issue? https://twitter.com/bradfitz/status/1341979944151207937?s=20
golang/go#37921
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we currently don't support
IPv4-mapped IPv6 addresses
today, we build out a separate ipv4 and ipv6 stack