diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 0f25930693..c25d63d8fc 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -1216,7 +1216,7 @@ func (f *fakeSandbox) SetKey(key string) error { return nil } -func (f *fakeSandbox) ResolveName(name string) []net.IP { +func (f *fakeSandbox) ResolveName(name string, ipType int) []net.IP { return nil } diff --git a/libnetwork/network.go b/libnetwork/network.go index aedc44f6a2..ed2b52c790 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -71,8 +71,9 @@ type NetworkInfo interface { type EndpointWalker func(ep Endpoint) bool type svcInfo struct { - svcMap map[string][]net.IP - ipMap map[string]string + svcMap map[string][]net.IP + svcIPv6Map map[string][]net.IP + ipMap map[string]string } // IpamConf contains all the ipam related configurations for a network @@ -894,68 +895,103 @@ func (n *network) EndpointByID(id string) (Endpoint, error) { } func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool) { + var ipv6 net.IP epName := ep.Name() if iface := ep.Iface(); iface.Address() != nil { myAliases := ep.MyAliases() + if iface.AddressIPv6() != nil { + ipv6 = iface.AddressIPv6().IP + } + if isAdd { // If anonymous endpoint has an alias use the first alias // for ip->name mapping. Not having the reverse mapping // breaks some apps if ep.isAnonymous() { if len(myAliases) > 0 { - n.addSvcRecords(myAliases[0], iface.Address().IP, true) + n.addSvcRecords(myAliases[0], iface.Address().IP, ipv6, true) } } else { - n.addSvcRecords(epName, iface.Address().IP, true) + n.addSvcRecords(epName, iface.Address().IP, ipv6, true) } for _, alias := range myAliases { - n.addSvcRecords(alias, iface.Address().IP, false) + n.addSvcRecords(alias, iface.Address().IP, ipv6, false) } } else { if ep.isAnonymous() { if len(myAliases) > 0 { - n.deleteSvcRecords(myAliases[0], iface.Address().IP, true) + n.deleteSvcRecords(myAliases[0], iface.Address().IP, ipv6, true) } } else { - n.deleteSvcRecords(epName, iface.Address().IP, true) + n.deleteSvcRecords(epName, iface.Address().IP, ipv6, true) } for _, alias := range myAliases { - n.deleteSvcRecords(alias, iface.Address().IP, false) + n.deleteSvcRecords(alias, iface.Address().IP, ipv6, false) } } } } -func (n *network) addSvcRecords(name string, epIP net.IP, ipMapUpdate bool) { +func addIPToName(ipMap map[string]string, name string, ip net.IP) { + reverseIP := netutils.ReverseIP(ip.String()) + if _, ok := ipMap[reverseIP]; !ok { + ipMap[reverseIP] = name + } +} + +func addNameToIP(svcMap map[string][]net.IP, name string, epIP net.IP) { + ipList := svcMap[name] + for _, ip := range ipList { + if ip.Equal(epIP) { + return + } + } + svcMap[name] = append(svcMap[name], epIP) +} + +func delNameToIP(svcMap map[string][]net.IP, name string, epIP net.IP) { + ipList := svcMap[name] + for i, ip := range ipList { + if ip.Equal(epIP) { + ipList = append(ipList[:i], ipList[i+1:]...) + break + } + } + svcMap[name] = ipList + + if len(ipList) == 0 { + delete(svcMap, name) + } +} + +func (n *network) addSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMapUpdate bool) { c := n.getController() c.Lock() defer c.Unlock() sr, ok := c.svcDb[n.ID()] if !ok { sr = svcInfo{ - svcMap: make(map[string][]net.IP), - ipMap: make(map[string]string), + svcMap: make(map[string][]net.IP), + svcIPv6Map: make(map[string][]net.IP), + ipMap: make(map[string]string), } c.svcDb[n.ID()] = sr } if ipMapUpdate { - reverseIP := netutils.ReverseIP(epIP.String()) - if _, ok := sr.ipMap[reverseIP]; !ok { - sr.ipMap[reverseIP] = name + addIPToName(sr.ipMap, name, epIP) + if epIPv6 != nil { + addIPToName(sr.ipMap, name, epIPv6) } } - ipList := sr.svcMap[name] - for _, ip := range ipList { - if ip.Equal(epIP) { - return - } + addNameToIP(sr.svcMap, name, epIP) + if epIPv6 != nil { + addNameToIP(sr.svcIPv6Map, name, epIPv6) } - sr.svcMap[name] = append(sr.svcMap[name], epIP) } -func (n *network) deleteSvcRecords(name string, epIP net.IP, ipMapUpdate bool) { +func (n *network) deleteSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMapUpdate bool) { c := n.getController() c.Lock() defer c.Unlock() @@ -966,19 +1002,16 @@ func (n *network) deleteSvcRecords(name string, epIP net.IP, ipMapUpdate bool) { if ipMapUpdate { delete(sr.ipMap, netutils.ReverseIP(epIP.String())) - } - ipList := sr.svcMap[name] - for i, ip := range ipList { - if ip.Equal(epIP) { - ipList = append(ipList[:i], ipList[i+1:]...) - break + if epIPv6 != nil { + delete(sr.ipMap, netutils.ReverseIP(epIPv6.String())) } } - sr.svcMap[name] = ipList - if len(ipList) == 0 { - delete(sr.svcMap, name) + delNameToIP(sr.svcMap, name, epIP) + + if epIPv6 != nil { + delNameToIP(sr.svcIPv6Map, name, epIPv6) } } diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index 854ef295bd..affbcd0f8f 100644 --- a/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -10,6 +10,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/iptables" + "github.com/docker/libnetwork/netutils" "github.com/miekg/dns" ) @@ -185,8 +186,8 @@ func shuffleAddr(addr []net.IP) []net.IP { return addr } -func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) { - addr := r.sb.ResolveName(name) +func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) { + addr := r.sb.ResolveName(name, ipType) if addr == nil { return nil, nil } @@ -200,12 +201,20 @@ func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error if len(addr) > 1 { addr = shuffleAddr(addr) } - - for _, ip := range addr { - rr := new(dns.A) - rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} - rr.A = ip - resp.Answer = append(resp.Answer, rr) + if ipType == netutils.IPv4 { + for _, ip := range addr { + rr := new(dns.A) + rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} + rr.A = ip + resp.Answer = append(resp.Answer, rr) + } + } else { + for _, ip := range addr { + rr := new(dns.AAAA) + rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL} + rr.AAAA = ip + resp.Answer = append(resp.Answer, rr) + } } return resp, nil } @@ -264,7 +273,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { } name := query.Question[0].Name if query.Question[0].Qtype == dns.TypeA { - resp, err = r.handleIPv4Query(name, query) + resp, err = r.handleIPQuery(name, query, netutils.IPv4) + } else if query.Question[0].Qtype == dns.TypeAAAA { + resp, err = r.handleIPQuery(name, query, netutils.IPv6) } else if query.Question[0].Qtype == dns.TypePTR { resp, err = r.handlePTRQuery(name, query) } diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index 5733deac18..8dea6462ba 100644 --- a/libnetwork/sandbox.go +++ b/libnetwork/sandbox.go @@ -11,6 +11,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" ) @@ -36,9 +37,9 @@ type Sandbox interface { Rename(name string) error // Delete destroys this container after detaching it from all connected endpoints. Delete() error - // ResolveName searches for the service name in the networks to which the sandbox - // is connected to. - ResolveName(name string) []net.IP + // ResolveName resolves a service name to an IPv4 or IPv6 address by searching the + // networks the sandbox is connected to. + ResolveName(name string, iplen int) []net.IP // ResolveIP returns the service name for the passed in IP. IP is in reverse dotted // notation; the format used for DNS PTR records ResolveIP(name string) string @@ -418,7 +419,7 @@ func (sb *sandbox) execFunc(f func()) { sb.osSbox.InvokeFunc(f) } -func (sb *sandbox) ResolveName(name string) []net.IP { +func (sb *sandbox) ResolveName(name string, ipType int) []net.IP { var ip []net.IP // Embedded server owns the docker network domain. Resolution should work @@ -453,13 +454,13 @@ func (sb *sandbox) ResolveName(name string) []net.IP { log.Debugf("To resolve: %v in %v", reqName[i], networkName[i]) // First check for local container alias - ip = sb.resolveName(reqName[i], networkName[i], epList, true) + ip = sb.resolveName(reqName[i], networkName[i], epList, true, ipType) if ip != nil { return ip } // Resolve the actual container name - ip = sb.resolveName(reqName[i], networkName[i], epList, false) + ip = sb.resolveName(reqName[i], networkName[i], epList, false, ipType) if ip != nil { return ip } @@ -467,7 +468,7 @@ func (sb *sandbox) ResolveName(name string) []net.IP { return nil } -func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) []net.IP { +func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool, ipType int) []net.IP { for _, ep := range epList { name := req n := ep.getNetwork() @@ -504,8 +505,13 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin continue } + var ip []net.IP n.Lock() - ip, ok := sr.svcMap[name] + if ipType == netutils.IPv6 { + ip, ok = sr.svcIPv6Map[name] + } else { + ip, ok = sr.svcMap[name] + } n.Unlock() if ok { return ip