- Fixes docker/docker#19576
- Fixed embedded DNS to listen in TCP as well
- Fixed a race-condition in IPAM to choose non-overlapping subnet for concurrent requests
Signed-off-by: Madhu Venugopal <madhu@docker.com>
| ... | ... |
@@ -27,7 +27,7 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de |
| 27 | 27 |
clone git github.com/imdario/mergo 0.2.1 |
| 28 | 28 |
|
| 29 | 29 |
#get libnetwork packages |
| 30 |
-clone git github.com/docker/libnetwork v0.6.0-rc2 |
|
| 30 |
+clone git github.com/docker/libnetwork v0.6.0-rc3 |
|
| 31 | 31 |
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec |
| 32 | 32 |
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b |
| 33 | 33 |
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 |
| ... | ... |
@@ -1,5 +1,10 @@ |
| 1 | 1 |
# Changelog |
| 2 | 2 |
|
| 3 |
+## 0.6.0-rc3 (2016-01-25) |
|
| 4 |
+- Fixes docker/docker#19576 |
|
| 5 |
+- Fixed embedded DNS to listen in TCP as well |
|
| 6 |
+- Fixed a race-condition in IPAM to choose non-overlapping subnet for concurrent requests |
|
| 7 |
+ |
|
| 3 | 8 |
## 0.6.0-rc2 (2016-01-21) |
| 4 | 9 |
- Fixes docker/docker#19376 |
| 5 | 10 |
- Fixes docker/docker#15819 |
| ... | ... |
@@ -63,7 +63,7 @@ run-tests: |
| 63 | 63 |
if ls $$dir/*.go &> /dev/null; then \ |
| 64 | 64 |
pushd . &> /dev/null ; \ |
| 65 | 65 |
cd $$dir ; \ |
| 66 |
- $(shell which godep) go test ${INSIDECONTAINER} -test.parallel 3 -test.v -covermode=count -coverprofile=./profile.tmp ; \
|
|
| 66 |
+ $(shell which godep) go test ${INSIDECONTAINER} -test.parallel 5 -test.v -covermode=count -coverprofile=./profile.tmp ; \
|
|
| 67 | 67 |
ret=$$? ;\ |
| 68 | 68 |
if [ $$ret -ne 0 ]; then exit $$ret; fi ;\ |
| 69 | 69 |
popd &> /dev/null; \ |
| ... | ... |
@@ -465,7 +465,7 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
|
| 465 | 465 |
if sb.needDefaultGW() {
|
| 466 | 466 |
return sb.setupDefaultGW(ep) |
| 467 | 467 |
} |
| 468 |
- return sb.clearDefaultGW() |
|
| 468 |
+ return nil |
|
| 469 | 469 |
} |
| 470 | 470 |
|
| 471 | 471 |
func (ep *endpoint) rename(name string) error {
|
| ... | ... |
@@ -597,15 +597,7 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) |
| 597 | 597 |
} |
| 598 | 598 |
|
| 599 | 599 |
sb.deleteHostsEntries(n.getSvcRecords(ep)) |
| 600 |
- |
|
| 601 |
- if !sb.inDelete && sb.needDefaultGW() {
|
|
| 602 |
- ep := sb.getEPwithoutGateway() |
|
| 603 |
- if ep == nil {
|
|
| 604 |
- return fmt.Errorf("endpoint without GW expected, but not found")
|
|
| 605 |
- } |
|
| 606 |
- return sb.setupDefaultGW(ep) |
|
| 607 |
- } |
|
| 608 |
- return sb.clearDefaultGW() |
|
| 600 |
+ return nil |
|
| 609 | 601 |
} |
| 610 | 602 |
|
| 611 | 603 |
func (n *network) validateForceDelete(locator string) error {
|
| ... | ... |
@@ -145,12 +145,12 @@ func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
| 145 | 145 |
// RequestPool returns an address pool along with its unique id. |
| 146 | 146 |
func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
| 147 | 147 |
log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6)
|
| 148 |
- k, nw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6) |
|
| 148 |
+retry: |
|
| 149 |
+ k, nw, ipr, pdf, err := a.parsePoolRequest(addressSpace, pool, subPool, v6) |
|
| 149 | 150 |
if err != nil {
|
| 150 | 151 |
return "", nil, nil, types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, pool, subPool, err)
|
| 151 | 152 |
} |
| 152 | 153 |
|
| 153 |
-retry: |
|
| 154 | 154 |
if err := a.refresh(addressSpace); err != nil {
|
| 155 | 155 |
return "", nil, nil, err |
| 156 | 156 |
} |
| ... | ... |
@@ -160,8 +160,12 @@ retry: |
| 160 | 160 |
return "", nil, nil, err |
| 161 | 161 |
} |
| 162 | 162 |
|
| 163 |
- insert, err := aSpace.updatePoolDBOnAdd(*k, nw, ipr) |
|
| 163 |
+ insert, err := aSpace.updatePoolDBOnAdd(*k, nw, ipr, pdf) |
|
| 164 | 164 |
if err != nil {
|
| 165 |
+ if _, ok := err.(types.MaskableError); ok {
|
|
| 166 |
+ log.Debugf("Retrying predefined pool search: %v", err)
|
|
| 167 |
+ goto retry |
|
| 168 |
+ } |
|
| 165 | 169 |
return "", nil, nil, err |
| 166 | 170 |
} |
| 167 | 171 |
|
| ... | ... |
@@ -221,38 +225,39 @@ func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
|
| 221 | 221 |
return aSpace, nil |
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 |
-func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, error) {
|
|
| 224 |
+func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, bool, error) {
|
|
| 225 | 225 |
var ( |
| 226 | 226 |
nw *net.IPNet |
| 227 | 227 |
ipr *AddressRange |
| 228 | 228 |
err error |
| 229 |
+ pdf = false |
|
| 229 | 230 |
) |
| 230 | 231 |
|
| 231 | 232 |
if addressSpace == "" {
|
| 232 |
- return nil, nil, nil, ipamapi.ErrInvalidAddressSpace |
|
| 233 |
+ return nil, nil, nil, false, ipamapi.ErrInvalidAddressSpace |
|
| 233 | 234 |
} |
| 234 | 235 |
|
| 235 | 236 |
if pool == "" && subPool != "" {
|
| 236 |
- return nil, nil, nil, ipamapi.ErrInvalidSubPool |
|
| 237 |
+ return nil, nil, nil, false, ipamapi.ErrInvalidSubPool |
|
| 237 | 238 |
} |
| 238 | 239 |
|
| 239 | 240 |
if pool != "" {
|
| 240 | 241 |
if _, nw, err = net.ParseCIDR(pool); err != nil {
|
| 241 |
- return nil, nil, nil, ipamapi.ErrInvalidPool |
|
| 242 |
+ return nil, nil, nil, false, ipamapi.ErrInvalidPool |
|
| 242 | 243 |
} |
| 243 | 244 |
if subPool != "" {
|
| 244 | 245 |
if ipr, err = getAddressRange(subPool, nw); err != nil {
|
| 245 |
- return nil, nil, nil, err |
|
| 246 |
+ return nil, nil, nil, false, err |
|
| 246 | 247 |
} |
| 247 | 248 |
} |
| 248 | 249 |
} else {
|
| 249 | 250 |
if nw, err = a.getPredefinedPool(addressSpace, v6); err != nil {
|
| 250 |
- return nil, nil, nil, err |
|
| 251 |
+ return nil, nil, nil, false, err |
|
| 251 | 252 |
} |
| 252 |
- |
|
| 253 |
+ pdf = true |
|
| 253 | 254 |
} |
| 254 | 255 |
|
| 255 |
- return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, ipr, nil
|
|
| 256 |
+ return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, ipr, pdf, nil
|
|
| 256 | 257 |
} |
| 257 | 258 |
|
| 258 | 259 |
func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
| ... | ... |
@@ -257,12 +257,15 @@ func (aSpace *addrSpace) New() datastore.KVObject {
|
| 257 | 257 |
} |
| 258 | 258 |
} |
| 259 | 259 |
|
| 260 |
-func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
|
| 260 |
+func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange, pdf bool) (func() error, error) {
|
|
| 261 | 261 |
aSpace.Lock() |
| 262 | 262 |
defer aSpace.Unlock() |
| 263 | 263 |
|
| 264 | 264 |
// Check if already allocated |
| 265 | 265 |
if p, ok := aSpace.subnets[k]; ok {
|
| 266 |
+ if pdf {
|
|
| 267 |
+ return nil, types.InternalMaskableErrorf("predefined pool %s is already reserved", nw)
|
|
| 268 |
+ } |
|
| 266 | 269 |
aSpace.incRefCount(p, 1) |
| 267 | 270 |
return func() error { return nil }, nil
|
| 268 | 271 |
} |
| ... | ... |
@@ -41,11 +41,13 @@ const ( |
| 41 | 41 |
|
| 42 | 42 |
// resolver implements the Resolver interface |
| 43 | 43 |
type resolver struct {
|
| 44 |
- sb *sandbox |
|
| 45 |
- extDNS []string |
|
| 46 |
- server *dns.Server |
|
| 47 |
- conn *net.UDPConn |
|
| 48 |
- err error |
|
| 44 |
+ sb *sandbox |
|
| 45 |
+ extDNS []string |
|
| 46 |
+ server *dns.Server |
|
| 47 |
+ conn *net.UDPConn |
|
| 48 |
+ tcpServer *dns.Server |
|
| 49 |
+ tcpListen *net.TCPListener |
|
| 50 |
+ err error |
|
| 49 | 51 |
} |
| 50 | 52 |
|
| 51 | 53 |
// NewResolver creates a new instance of the Resolver |
| ... | ... |
@@ -60,6 +62,7 @@ func (r *resolver) SetupFunc() func() {
|
| 60 | 60 |
return (func() {
|
| 61 | 61 |
var err error |
| 62 | 62 |
|
| 63 |
+ // DNS operates primarily on UDP |
|
| 63 | 64 |
addr := &net.UDPAddr{
|
| 64 | 65 |
IP: net.ParseIP(resolverIP), |
| 65 | 66 |
} |
| ... | ... |
@@ -72,9 +75,23 @@ func (r *resolver) SetupFunc() func() {
|
| 72 | 72 |
laddr := r.conn.LocalAddr() |
| 73 | 73 |
_, ipPort, _ := net.SplitHostPort(laddr.String()) |
| 74 | 74 |
|
| 75 |
+ // Listen on a TCP as well |
|
| 76 |
+ tcpaddr := &net.TCPAddr{
|
|
| 77 |
+ IP: net.ParseIP(resolverIP), |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
|
|
| 81 |
+ if err != nil {
|
|
| 82 |
+ r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
|
|
| 83 |
+ return |
|
| 84 |
+ } |
|
| 85 |
+ ltcpaddr := r.tcpListen.Addr() |
|
| 86 |
+ _, tcpPort, _ := net.SplitHostPort(ltcpaddr.String()) |
|
| 75 | 87 |
rules := [][]string{
|
| 76 | 88 |
{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
|
| 77 | 89 |
{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
| 90 |
+ {"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", ltcpaddr.String()},
|
|
| 91 |
+ {"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
|
| 78 | 92 |
} |
| 79 | 93 |
|
| 80 | 94 |
for _, rule := range rules {
|
| ... | ... |
@@ -97,6 +114,12 @@ func (r *resolver) Start() error {
|
| 97 | 97 |
go func() {
|
| 98 | 98 |
s.ActivateAndServe() |
| 99 | 99 |
}() |
| 100 |
+ |
|
| 101 |
+ tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen}
|
|
| 102 |
+ r.tcpServer = tcpServer |
|
| 103 |
+ go func() {
|
|
| 104 |
+ tcpServer.ActivateAndServe() |
|
| 105 |
+ }() |
|
| 100 | 106 |
return nil |
| 101 | 107 |
} |
| 102 | 108 |
|
| ... | ... |
@@ -104,7 +127,11 @@ func (r *resolver) Stop() {
|
| 104 | 104 |
if r.server != nil {
|
| 105 | 105 |
r.server.Shutdown() |
| 106 | 106 |
} |
| 107 |
+ if r.tcpServer != nil {
|
|
| 108 |
+ r.tcpServer.Shutdown() |
|
| 109 |
+ } |
|
| 107 | 110 |
r.conn = nil |
| 111 |
+ r.tcpServer = nil |
|
| 108 | 112 |
r.err = fmt.Errorf("setup not done yet")
|
| 109 | 113 |
} |
| 110 | 114 |
|
| ... | ... |
@@ -195,9 +222,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
| 195 | 195 |
num = len(r.extDNS) |
| 196 | 196 |
} |
| 197 | 197 |
for i := 0; i < num; i++ {
|
| 198 |
- log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[i], name, query.Question[0].Qtype)
|
|
| 198 |
+ log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)
|
|
| 199 | 199 |
|
| 200 |
- c := &dns.Client{Net: "udp"}
|
|
| 200 |
+ c := &dns.Client{Net: w.LocalAddr().Network()}
|
|
| 201 | 201 |
addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)
|
| 202 | 202 |
|
| 203 | 203 |
resp, _, err = c.Exchange(query, addr) |
| ... | ... |
@@ -186,12 +186,6 @@ func (sb *sandbox) delete(force bool) error {
|
| 186 | 186 |
// Detach from all endpoints |
| 187 | 187 |
retain := false |
| 188 | 188 |
for _, ep := range sb.getConnectedEndpoints() {
|
| 189 |
- // endpoint in the Gateway network will be cleaned up |
|
| 190 |
- // when when sandbox no longer needs external connectivity |
|
| 191 |
- if ep.endpointInGWNetwork() {
|
|
| 192 |
- continue |
|
| 193 |
- } |
|
| 194 |
- |
|
| 195 | 189 |
// Retain the sanbdox if we can't obtain the network from store. |
| 196 | 190 |
if _, err := c.getNetworkFromStore(ep.getNetwork().ID()); err != nil {
|
| 197 | 191 |
retain = true |