Brings in https://github.com/moby/libnetwork/pull/2604
Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>
... | ... |
@@ -3,7 +3,7 @@ |
3 | 3 |
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When |
4 | 4 |
# updating the binary version, consider updating github.com/docker/libnetwork |
5 | 5 |
# in vendor.conf accordingly |
6 |
-: "${LIBNETWORK_COMMIT:=5c6a95bfb20c61571a00f913c6b91959ede84e8d}" |
|
6 |
+: "${LIBNETWORK_COMMIT:=fa125a3512ee0f6187721c88582bf8c4378bd4d7}" |
|
7 | 7 |
|
8 | 8 |
install_proxy() { |
9 | 9 |
case "$1" in |
... | ... |
@@ -47,7 +47,7 @@ github.com/grpc-ecosystem/go-grpc-middleware 3c51f7f332123e8be5a157c0802a |
47 | 47 |
# libnetwork |
48 | 48 |
|
49 | 49 |
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly |
50 |
-github.com/docker/libnetwork 5c6a95bfb20c61571a00f913c6b91959ede84e8d |
|
50 |
+github.com/docker/libnetwork fa125a3512ee0f6187721c88582bf8c4378bd4d7 |
|
51 | 51 |
github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f |
52 | 52 |
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 |
53 | 53 |
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec |
... | ... |
@@ -11,71 +11,113 @@ import ( |
11 | 11 |
"github.com/sirupsen/logrus" |
12 | 12 |
) |
13 | 13 |
|
14 |
-var ( |
|
15 |
- defaultBindingIP = net.IPv4(0, 0, 0, 0) |
|
16 |
- defaultBindingIPV6 = net.ParseIP("::") |
|
17 |
-) |
|
18 |
- |
|
19 | 14 |
func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { |
20 | 15 |
if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { |
21 | 16 |
return nil, nil |
22 | 17 |
} |
23 | 18 |
|
24 |
- defHostIP := defaultBindingIP |
|
19 |
+ defHostIP := net.IPv4zero // 0.0.0.0 |
|
25 | 20 |
if reqDefBindIP != nil { |
26 | 21 |
defHostIP = reqDefBindIP |
27 | 22 |
} |
28 | 23 |
|
29 |
- // IPv4 port binding including user land proxy |
|
30 |
- pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) |
|
31 |
- if err != nil { |
|
32 |
- return nil, err |
|
24 |
+ var containerIPv6 net.IP |
|
25 |
+ if ep.addrv6 != nil { |
|
26 |
+ containerIPv6 = ep.addrv6.IP |
|
33 | 27 |
} |
34 | 28 |
|
35 |
- // IPv6 port binding excluding user land proxy |
|
36 |
- if n.driver.config.EnableIP6Tables && ep.addrv6 != nil { |
|
37 |
- // TODO IPv6 custom default binding IP |
|
38 |
- pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false) |
|
39 |
- if err != nil { |
|
40 |
- // ensure we clear the previous allocated IPv4 ports |
|
41 |
- n.releasePortsInternal(pb) |
|
42 |
- return nil, err |
|
43 |
- } |
|
44 |
- |
|
45 |
- pb = append(pb, pbv6...) |
|
29 |
+ pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled) |
|
30 |
+ if err != nil { |
|
31 |
+ return nil, err |
|
46 | 32 |
} |
47 | 33 |
return pb, nil |
48 | 34 |
} |
49 | 35 |
|
50 |
-func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { |
|
36 |
+func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { |
|
51 | 37 |
bs := make([]types.PortBinding, 0, len(bindings)) |
52 | 38 |
for _, c := range bindings { |
53 |
- b := c.GetCopy() |
|
54 |
- if err := n.allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil { |
|
55 |
- // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
56 |
- if cuErr := n.releasePortsInternal(bs); cuErr != nil { |
|
57 |
- logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr) |
|
39 |
+ bIPv4 := c.GetCopy() |
|
40 |
+ bIPv6 := c.GetCopy() |
|
41 |
+ // Allocate IPv4 Port mappings |
|
42 |
+ if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok { |
|
43 |
+ if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil { |
|
44 |
+ // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
45 |
+ if cuErr := n.releasePortsInternal(bs); cuErr != nil { |
|
46 |
+ logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr) |
|
47 |
+ } |
|
48 |
+ return nil, err |
|
58 | 49 |
} |
59 |
- return nil, err |
|
50 |
+ bs = append(bs, bIPv4) |
|
51 |
+ } |
|
52 |
+ // Allocate IPv6 Port mappings |
|
53 |
+ if ok := n.validatePortBindingIPv6(&bIPv6, containerIPv6, defHostIP); ok { |
|
54 |
+ if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil { |
|
55 |
+ // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
56 |
+ if cuErr := n.releasePortsInternal(bs); cuErr != nil { |
|
57 |
+ logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr) |
|
58 |
+ } |
|
59 |
+ return nil, err |
|
60 |
+ } |
|
61 |
+ bs = append(bs, bIPv6) |
|
60 | 62 |
} |
61 |
- bs = append(bs, b) |
|
62 | 63 |
} |
63 | 64 |
return bs, nil |
64 | 65 |
} |
65 | 66 |
|
66 |
-func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error { |
|
67 |
- var ( |
|
68 |
- host net.Addr |
|
69 |
- err error |
|
70 |
- ) |
|
71 |
- |
|
72 |
- // Store the container interface address in the operational binding |
|
73 |
- bnd.IP = containerIP |
|
74 |
- |
|
67 |
+// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true |
|
68 |
+// if this is a valid IPv4 binding, else returns false |
|
69 |
+func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool { |
|
70 |
+ //Return early if there is a valid Host IP, but its not a IPv6 address |
|
71 |
+ if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil { |
|
72 |
+ return false |
|
73 |
+ } |
|
75 | 74 |
// Adjust the host address in the operational binding |
76 | 75 |
if len(bnd.HostIP) == 0 { |
76 |
+ // Return early if the default binding address is an IPv6 address |
|
77 |
+ if defHostIP.To4() == nil { |
|
78 |
+ return false |
|
79 |
+ } |
|
77 | 80 |
bnd.HostIP = defHostIP |
78 | 81 |
} |
82 |
+ bnd.IP = containerIPv4 |
|
83 |
+ return true |
|
84 |
+ |
|
85 |
+} |
|
86 |
+ |
|
87 |
+// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true |
|
88 |
+// if this is a valid IP6v binding, else returns false |
|
89 |
+func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIPv6, defHostIP net.IP) bool { |
|
90 |
+ // Return early if there is no IPv6 container endpoint |
|
91 |
+ if containerIPv6 == nil { |
|
92 |
+ return false |
|
93 |
+ } |
|
94 |
+ // Return early if there is a valid Host IP, which is a IPv4 address |
|
95 |
+ if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil { |
|
96 |
+ return false |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ // Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0 |
|
100 |
+ if len(bnd.HostIP) == 0 { |
|
101 |
+ if defHostIP.Equal(net.IPv4zero) { |
|
102 |
+ bnd.HostIP = net.IPv6zero |
|
103 |
+ // If the default binding IP is an IPv6 address, use it |
|
104 |
+ } else if defHostIP.To4() == nil { |
|
105 |
+ bnd.HostIP = defHostIP |
|
106 |
+ // Return false if default binding ip is an IPv4 address |
|
107 |
+ } else { |
|
108 |
+ return false |
|
109 |
+ } |
|
110 |
+ } |
|
111 |
+ bnd.IP = containerIPv6 |
|
112 |
+ return true |
|
113 |
+ |
|
114 |
+} |
|
115 |
+ |
|
116 |
+func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error { |
|
117 |
+ var ( |
|
118 |
+ host net.Addr |
|
119 |
+ err error |
|
120 |
+ ) |
|
79 | 121 |
|
80 | 122 |
// Adjust HostPortEnd if this is not a range. |
81 | 123 |
if bnd.HostPortEnd == 0 { |
... | ... |
@@ -90,7 +132,7 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos |
90 | 90 |
|
91 | 91 |
portmapper := n.portMapper |
92 | 92 |
|
93 |
- if containerIP.To4() == nil { |
|
93 |
+ if bnd.IP.To4() == nil { |
|
94 | 94 |
portmapper = n.portMapperV6 |
95 | 95 |
} |
96 | 96 |
|
... | ... |
@@ -151,20 +151,16 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, |
151 | 151 |
} |
152 | 152 |
|
153 | 153 |
containerIP, containerPort := getIPAndPort(m.container) |
154 |
- if pm.checkIP(hostIP) { |
|
155 |
- if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { |
|
156 |
- return nil, err |
|
157 |
- } |
|
154 |
+ if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { |
|
155 |
+ return nil, err |
|
158 | 156 |
} |
159 | 157 |
|
160 | 158 |
cleanup := func() error { |
161 | 159 |
// need to undo the iptables rules before we return |
162 | 160 |
m.userlandProxy.Stop() |
163 |
- if pm.checkIP(hostIP) { |
|
164 |
- pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) |
|
165 |
- if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { |
|
166 |
- return err |
|
167 |
- } |
|
161 |
+ pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) |
|
162 |
+ if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { |
|
163 |
+ return err |
|
168 | 164 |
} |
169 | 165 |
|
170 | 166 |
return nil |
... | ... |
@@ -44,11 +44,3 @@ func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net |
44 | 44 |
} |
45 | 45 |
return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName) |
46 | 46 |
} |
47 |
- |
|
48 |
-// checkIP checks if IP is valid and matching to chain version |
|
49 |
-func (pm *PortMapper) checkIP(ip net.IP) bool { |
|
50 |
- if pm.chain == nil || pm.chain.IPTable.Version == iptables.IPv4 { |
|
51 |
- return ip.To4() != nil |
|
52 |
- } |
|
53 |
- return ip.To16() != nil |
|
54 |
-} |
... | ... |
@@ -19,6 +19,16 @@ type userlandProxy interface { |
19 | 19 |
Stop() error |
20 | 20 |
} |
21 | 21 |
|
22 |
+// ipVersion refers to IP version - v4 or v6 |
|
23 |
+type ipVersion string |
|
24 |
+ |
|
25 |
+const ( |
|
26 |
+ // IPv4 is version 4 |
|
27 |
+ ipv4 ipVersion = "4" |
|
28 |
+ // IPv4 is version 6 |
|
29 |
+ ipv6 ipVersion = "6" |
|
30 |
+) |
|
31 |
+ |
|
22 | 32 |
// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP |
23 | 33 |
// proxies as separate processes. |
24 | 34 |
type proxyCommand struct { |
... | ... |
@@ -77,21 +87,27 @@ func (p *proxyCommand) Stop() error { |
77 | 77 |
// port allocations on bound port, because without userland proxy we using |
78 | 78 |
// iptables rules and not net.Listen |
79 | 79 |
type dummyProxy struct { |
80 |
- listener io.Closer |
|
81 |
- addr net.Addr |
|
80 |
+ listener io.Closer |
|
81 |
+ addr net.Addr |
|
82 |
+ ipVersion ipVersion |
|
82 | 83 |
} |
83 | 84 |
|
84 | 85 |
func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) { |
86 |
+ // detect version of hostIP to bind only to correct version |
|
87 |
+ version := ipv4 |
|
88 |
+ if hostIP.To4() == nil { |
|
89 |
+ version = ipv6 |
|
90 |
+ } |
|
85 | 91 |
switch proto { |
86 | 92 |
case "tcp": |
87 | 93 |
addr := &net.TCPAddr{IP: hostIP, Port: hostPort} |
88 |
- return &dummyProxy{addr: addr}, nil |
|
94 |
+ return &dummyProxy{addr: addr, ipVersion: version}, nil |
|
89 | 95 |
case "udp": |
90 | 96 |
addr := &net.UDPAddr{IP: hostIP, Port: hostPort} |
91 |
- return &dummyProxy{addr: addr}, nil |
|
97 |
+ return &dummyProxy{addr: addr, ipVersion: version}, nil |
|
92 | 98 |
case "sctp": |
93 | 99 |
addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort} |
94 |
- return &dummyProxy{addr: addr}, nil |
|
100 |
+ return &dummyProxy{addr: addr, ipVersion: version}, nil |
|
95 | 101 |
default: |
96 | 102 |
return nil, fmt.Errorf("Unknown addr type: %s", proto) |
97 | 103 |
} |
... | ... |
@@ -100,19 +116,19 @@ func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, er |
100 | 100 |
func (p *dummyProxy) Start() error { |
101 | 101 |
switch addr := p.addr.(type) { |
102 | 102 |
case *net.TCPAddr: |
103 |
- l, err := net.ListenTCP("tcp", addr) |
|
103 |
+ l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr) |
|
104 | 104 |
if err != nil { |
105 | 105 |
return err |
106 | 106 |
} |
107 | 107 |
p.listener = l |
108 | 108 |
case *net.UDPAddr: |
109 |
- l, err := net.ListenUDP("udp", addr) |
|
109 |
+ l, err := net.ListenUDP("udp"+string(p.ipVersion), addr) |
|
110 | 110 |
if err != nil { |
111 | 111 |
return err |
112 | 112 |
} |
113 | 113 |
p.listener = l |
114 | 114 |
case *sctp.SCTPAddr: |
115 |
- l, err := sctp.ListenSCTP("sctp", addr) |
|
115 |
+ l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr) |
|
116 | 116 |
if err != nil { |
117 | 117 |
return err |
118 | 118 |
} |