Browse code

Embedded DNS server

Signed-off-by: Santhosh Manohar <santhosh@docker.com>

Santhosh Manohar authored on 2015/12/24 18:51:32
Showing 10 changed files
... ...
@@ -143,7 +143,7 @@ type controller struct {
143 143
 	extKeyListener net.Listener
144 144
 	watchCh        chan *endpoint
145 145
 	unWatchCh      chan *endpoint
146
-	svcDb          map[string]svcMap
146
+	svcDb          map[string]svcInfo
147 147
 	nmap           map[string]*netWatch
148 148
 	defOsSbox      osl.Sandbox
149 149
 	sboxOnce       sync.Once
... ...
@@ -171,7 +171,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
171 171
 		sandboxes:   sandboxTable{},
172 172
 		drivers:     driverTable{},
173 173
 		ipamDrivers: ipamTable{},
174
-		svcDb:       make(map[string]svcMap),
174
+		svcDb:       make(map[string]svcInfo),
175 175
 	}
176 176
 
177 177
 	if err := c.initStores(); err != nil {
... ...
@@ -50,21 +50,22 @@ type Endpoint interface {
50 50
 type EndpointOption func(ep *endpoint)
51 51
 
52 52
 type endpoint struct {
53
-	name          string
54
-	id            string
55
-	network       *network
56
-	iface         *endpointInterface
57
-	joinInfo      *endpointJoinInfo
58
-	sandboxID     string
59
-	exposedPorts  []types.TransportPort
60
-	anonymous     bool
61
-	generic       map[string]interface{}
62
-	joinLeaveDone chan struct{}
63
-	prefAddress   net.IP
64
-	prefAddressV6 net.IP
65
-	ipamOptions   map[string]string
66
-	dbIndex       uint64
67
-	dbExists      bool
53
+	name              string
54
+	id                string
55
+	network           *network
56
+	iface             *endpointInterface
57
+	joinInfo          *endpointJoinInfo
58
+	sandboxID         string
59
+	exposedPorts      []types.TransportPort
60
+	anonymous         bool
61
+	disableResolution bool
62
+	generic           map[string]interface{}
63
+	joinLeaveDone     chan struct{}
64
+	prefAddress       net.IP
65
+	prefAddressV6     net.IP
66
+	ipamOptions       map[string]string
67
+	dbIndex           uint64
68
+	dbExists          bool
68 69
 	sync.Mutex
69 70
 }
70 71
 
... ...
@@ -82,6 +83,7 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
82 82
 	}
83 83
 	epMap["sandbox"] = ep.sandboxID
84 84
 	epMap["anonymous"] = ep.anonymous
85
+	epMap["disableResolution"] = ep.disableResolution
85 86
 	return json.Marshal(epMap)
86 87
 }
87 88
 
... ...
@@ -159,6 +161,9 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
159 159
 	if v, ok := epMap["anonymous"]; ok {
160 160
 		ep.anonymous = v.(bool)
161 161
 	}
162
+	if v, ok := epMap["disableResolution"]; ok {
163
+		ep.disableResolution = v.(bool)
164
+	}
162 165
 	return nil
163 166
 }
164 167
 
... ...
@@ -177,6 +182,7 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
177 177
 	dstEp.dbIndex = ep.dbIndex
178 178
 	dstEp.dbExists = ep.dbExists
179 179
 	dstEp.anonymous = ep.anonymous
180
+	dstEp.disableResolution = ep.disableResolution
180 181
 
181 182
 	if ep.iface != nil {
182 183
 		dstEp.iface = &endpointInterface{}
... ...
@@ -222,6 +228,12 @@ func (ep *endpoint) isAnonymous() bool {
222 222
 	return ep.anonymous
223 223
 }
224 224
 
225
+func (ep *endpoint) needResolver() bool {
226
+	ep.Lock()
227
+	defer ep.Unlock()
228
+	return !ep.disableResolution
229
+}
230
+
225 231
 // endpoint Key structure : endpoint/network-id/endpoint-id
226 232
 func (ep *endpoint) Key() []string {
227 233
 	if ep.network == nil {
... ...
@@ -396,10 +408,9 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
396 396
 	if ip := ep.getFirstInterfaceAddress(); ip != nil {
397 397
 		address = ip.String()
398 398
 	}
399
-	if err = sb.updateHostsFile(address, network.getSvcRecords(ep)); err != nil {
399
+	if err = sb.updateHostsFile(address); err != nil {
400 400
 		return err
401 401
 	}
402
-
403 402
 	if err = sb.updateDNS(network.enableIPv6); err != nil {
404 403
 		return err
405 404
 	}
... ...
@@ -729,6 +740,14 @@ func CreateOptionAnonymous() EndpointOption {
729 729
 	}
730 730
 }
731 731
 
732
+// CreateOptionDisableResolution function returns an option setter to indicate
733
+// this endpoint doesn't want embedded DNS server functionality
734
+func CreateOptionDisableResolution() EndpointOption {
735
+	return func(ep *endpoint) {
736
+		ep.disableResolution = true
737
+	}
738
+}
739
+
732 740
 // JoinOptionPriority function returns an option setter for priority option to
733 741
 // be passed to the endpoint.Join() method.
734 742
 func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {
... ...
@@ -1213,6 +1213,14 @@ func (f *fakeSandbox) SetKey(key string) error {
1213 1213
 	return nil
1214 1214
 }
1215 1215
 
1216
+func (f *fakeSandbox) ResolveName(name string) net.IP {
1217
+	return nil
1218
+}
1219
+
1220
+func (f *fakeSandbox) ResolveIP(ip string) string {
1221
+	return ""
1222
+}
1223
+
1216 1224
 func TestExternalKey(t *testing.T) {
1217 1225
 	externalKeyTest(t, false)
1218 1226
 }
... ...
@@ -1698,6 +1706,7 @@ func TestEnableIPv6(t *testing.T) {
1698 1698
 	}
1699 1699
 
1700 1700
 	tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n")
1701
+	expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n")
1701 1702
 	//take a copy of resolv.conf for restoring after test completes
1702 1703
 	resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
1703 1704
 	if err != nil {
... ...
@@ -1760,8 +1769,8 @@ func TestEnableIPv6(t *testing.T) {
1760 1760
 		t.Fatal(err)
1761 1761
 	}
1762 1762
 
1763
-	if !bytes.Equal(content, tmpResolvConf) {
1764
-		t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf), string(content))
1763
+	if !bytes.Equal(content, expectedResolvConf) {
1764
+		t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf), string(content))
1765 1765
 	}
1766 1766
 
1767 1767
 	if err != nil {
... ...
@@ -1793,7 +1802,7 @@ func TestResolvConfHost(t *testing.T) {
1793 1793
 		t.Fatal(err)
1794 1794
 	}
1795 1795
 
1796
-	ep1, err := n.CreateEndpoint("ep1", nil)
1796
+	ep1, err := n.CreateEndpoint("ep1", libnetwork.CreateOptionDisableResolution())
1797 1797
 	if err != nil {
1798 1798
 		t.Fatal(err)
1799 1799
 	}
... ...
@@ -1854,9 +1863,8 @@ func TestResolvConf(t *testing.T) {
1854 1854
 	}
1855 1855
 
1856 1856
 	tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n")
1857
-	expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n")
1858 1857
 	tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888\n")
1859
-	expectedResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\n")
1858
+	expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n")
1860 1859
 	tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n")
1861 1860
 
1862 1861
 	//take a copy of resolv.conf for restoring after test completes
... ...
@@ -1965,8 +1973,8 @@ func TestResolvConf(t *testing.T) {
1965 1965
 		t.Fatal(err)
1966 1966
 	}
1967 1967
 
1968
-	if !bytes.Equal(content, expectedResolvConf2) {
1969
-		t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf2), string(content))
1968
+	if !bytes.Equal(content, expectedResolvConf1) {
1969
+		t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf1), string(content))
1970 1970
 	}
1971 1971
 
1972 1972
 	if err := ioutil.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil {
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"fmt"
10 10
 	"io"
11 11
 	"net"
12
+	"strings"
12 13
 
13 14
 	"github.com/docker/libnetwork/types"
14 15
 )
... ...
@@ -132,3 +133,39 @@ func GenerateRandomName(prefix string, size int) (string, error) {
132 132
 	}
133 133
 	return prefix + hex.EncodeToString(id)[:size], nil
134 134
 }
135
+
136
+// ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in
137
+// the dotted decimal form . This is used to setup the IP to service name mapping in the optimal
138
+// way for the DNS PTR queries.
139
+func ReverseIP(IP string) string {
140
+	var reverseIP []string
141
+
142
+	if net.ParseIP(IP).To4() != nil {
143
+		reverseIP = strings.Split(IP, ".")
144
+		l := len(reverseIP)
145
+		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
146
+			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
147
+		}
148
+	} else {
149
+		reverseIP = strings.Split(IP, ":")
150
+
151
+		// Reversed IPv6 is represented in dotted decimal instead of the typical
152
+		// colon hex notation
153
+		for key := range reverseIP {
154
+			if len(reverseIP[key]) == 0 { // expand the compressed 0s
155
+				reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":"))
156
+			} else if len(reverseIP[key]) < 4 { // 0-padding needed
157
+				reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key]
158
+			}
159
+		}
160
+
161
+		reverseIP = strings.Split(strings.Join(reverseIP, ""), "")
162
+
163
+		l := len(reverseIP)
164
+		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
165
+			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
166
+		}
167
+	}
168
+
169
+	return strings.Join(reverseIP, ".")
170
+}
... ...
@@ -69,7 +69,10 @@ type NetworkInfo interface {
69 69
 // When the function returns true, the walk will stop.
70 70
 type EndpointWalker func(ep Endpoint) bool
71 71
 
72
-type svcMap map[string]net.IP
72
+type svcInfo struct {
73
+	svcMap map[string]net.IP
74
+	ipMap  map[string]string
75
+}
73 76
 
74 77
 // IpamConf contains all the ipam related configurations for a network
75 78
 type IpamConf struct {
... ...
@@ -159,7 +162,6 @@ type network struct {
159 159
 	epCnt        *endpointCnt
160 160
 	generic      options.Generic
161 161
 	dbIndex      uint64
162
-	svcRecords   svcMap
163 162
 	dbExists     bool
164 163
 	persist      bool
165 164
 	stopWatchCh  chan struct{}
... ...
@@ -832,62 +834,33 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool
832 832
 	c := n.getController()
833 833
 	sr, ok := c.svcDb[n.ID()]
834 834
 	if !ok {
835
-		c.svcDb[n.ID()] = svcMap{}
835
+		c.svcDb[n.ID()] = svcInfo{
836
+			svcMap: make(map[string]net.IP),
837
+			ipMap:  make(map[string]string),
838
+		}
836 839
 		sr = c.svcDb[n.ID()]
837 840
 	}
838 841
 
842
+	epName := ep.Name()
839 843
 	n.Lock()
840
-	var recs []etchosts.Record
841 844
 	if iface := ep.Iface(); iface.Address() != nil {
845
+
846
+		reverseIP := netutils.ReverseIP(iface.Address().IP.String())
842 847
 		if isAdd {
843 848
 			// If we already have this endpoint in service db just return
844
-			if _, ok := sr[ep.Name()]; ok {
849
+			if _, ok := sr.svcMap[epName]; ok {
845 850
 				n.Unlock()
846 851
 				return
847 852
 			}
848 853
 
849
-			sr[ep.Name()] = iface.Address().IP
850
-			sr[ep.Name()+"."+n.name] = iface.Address().IP
854
+			sr.svcMap[epName] = iface.Address().IP
855
+			sr.ipMap[reverseIP] = epName
851 856
 		} else {
852
-			delete(sr, ep.Name())
853
-			delete(sr, ep.Name()+"."+n.name)
857
+			delete(sr.svcMap, epName)
858
+			delete(sr.ipMap, reverseIP)
854 859
 		}
855
-
856
-		recs = append(recs, etchosts.Record{
857
-			Hosts: ep.Name(),
858
-			IP:    iface.Address().IP.String(),
859
-		})
860
-
861
-		recs = append(recs, etchosts.Record{
862
-			Hosts: ep.Name() + "." + n.name,
863
-			IP:    iface.Address().IP.String(),
864
-		})
865 860
 	}
866 861
 	n.Unlock()
867
-
868
-	// If there are no records to add or delete then simply return here
869
-	if len(recs) == 0 {
870
-		return
871
-	}
872
-
873
-	var sbList []*sandbox
874
-	for _, lEp := range localEps {
875
-		if ep.ID() == lEp.ID() {
876
-			continue
877
-		}
878
-
879
-		if sb, hasSandbox := lEp.getSandbox(); hasSandbox {
880
-			sbList = append(sbList, sb)
881
-		}
882
-	}
883
-
884
-	for _, sb := range sbList {
885
-		if isAdd {
886
-			sb.addHostsEntries(recs)
887
-		} else {
888
-			sb.deleteHostsEntries(recs)
889
-		}
890
-	}
891 862
 }
892 863
 
893 864
 func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
... ...
@@ -897,7 +870,7 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
897 897
 	var recs []etchosts.Record
898 898
 	sr, _ := n.ctrlr.svcDb[n.id]
899 899
 
900
-	for h, ip := range sr {
900
+	for h, ip := range sr.svcMap {
901 901
 		if ep != nil && strings.Split(h, ".")[0] == ep.Name() {
902 902
 			continue
903 903
 		}
904 904
new file mode 100644
... ...
@@ -0,0 +1,205 @@
0
+package libnetwork
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+	"strings"
6
+
7
+	log "github.com/Sirupsen/logrus"
8
+	"github.com/docker/libnetwork/iptables"
9
+	"github.com/miekg/dns"
10
+)
11
+
12
+// Resolver represents the embedded DNS server in Docker. It operates
13
+// by listening on container's loopback interface for DNS queries.
14
+type Resolver interface {
15
+	// Start starts the name server for the container
16
+	Start() error
17
+	// Stop stops the name server for the container
18
+	Stop()
19
+	// SetupFunc() provides the setup function that should be run
20
+	// in the container's network namespace.
21
+	SetupFunc() func()
22
+	// NameServer() returns the IP of the DNS resolver for the
23
+	// containers.
24
+	NameServer() string
25
+	// To configure external name servers the resolver should use
26
+	SetExtServers([]string)
27
+	// ResolverOptions returns resolv.conf options that should be set
28
+	ResolverOptions() []string
29
+}
30
+
31
+const (
32
+	resolverIP    = "127.0.0.11"
33
+	dnsPort       = "53"
34
+	ptrIPv4domain = ".in-addr.arpa."
35
+	ptrIPv6domain = ".ip6.arpa."
36
+	respTTL       = 1800
37
+)
38
+
39
+// resolver implements the Resolver interface
40
+type resolver struct {
41
+	sb     *sandbox
42
+	extDNS []string
43
+	server *dns.Server
44
+	conn   *net.UDPConn
45
+	err    error
46
+}
47
+
48
+// NewResolver creates a new instance of the Resolver
49
+func NewResolver(sb *sandbox) Resolver {
50
+	return &resolver{
51
+		sb:  sb,
52
+		err: fmt.Errorf("setup not done yet"),
53
+	}
54
+}
55
+
56
+func (r *resolver) SetupFunc() func() {
57
+	return (func() {
58
+		var err error
59
+
60
+		addr := &net.UDPAddr{
61
+			IP: net.ParseIP(resolverIP),
62
+		}
63
+
64
+		r.conn, err = net.ListenUDP("udp", addr)
65
+		if err != nil {
66
+			r.err = fmt.Errorf("error in opening name server socket %v", err)
67
+			return
68
+		}
69
+		laddr := r.conn.LocalAddr()
70
+		_, ipPort, _ := net.SplitHostPort(laddr.String())
71
+
72
+		rules := [][]string{
73
+			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
74
+			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
75
+		}
76
+
77
+		for _, rule := range rules {
78
+			r.err = iptables.RawCombinedOutput(rule...)
79
+			if r.err != nil {
80
+				return
81
+			}
82
+		}
83
+		r.err = nil
84
+	})
85
+}
86
+
87
+func (r *resolver) Start() error {
88
+	// make sure the resolver has been setup before starting
89
+	if r.err != nil {
90
+		return r.err
91
+	}
92
+	s := &dns.Server{Handler: r, PacketConn: r.conn}
93
+	r.server = s
94
+	go func() {
95
+		s.ActivateAndServe()
96
+	}()
97
+	return nil
98
+}
99
+
100
+func (r *resolver) Stop() {
101
+	if r.server != nil {
102
+		r.server.Shutdown()
103
+	}
104
+}
105
+
106
+func (r *resolver) SetExtServers(dns []string) {
107
+	r.extDNS = dns
108
+}
109
+
110
+func (r *resolver) NameServer() string {
111
+	return resolverIP
112
+}
113
+
114
+func (r *resolver) ResolverOptions() []string {
115
+	return []string{"ndots:0"}
116
+}
117
+
118
+func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
119
+	addr := r.sb.ResolveName(name)
120
+	if addr == nil {
121
+		return nil, nil
122
+	}
123
+
124
+	log.Debugf("Lookup for %s: IP %s", name, addr.String())
125
+
126
+	resp := new(dns.Msg)
127
+	resp.SetReply(query)
128
+
129
+	rr := new(dns.A)
130
+	rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
131
+	rr.A = addr
132
+	resp.Answer = append(resp.Answer, rr)
133
+	return resp, nil
134
+}
135
+
136
+func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
137
+	parts := []string{}
138
+
139
+	if strings.HasSuffix(ptr, ptrIPv4domain) {
140
+		parts = strings.Split(ptr, ptrIPv4domain)
141
+	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
142
+		parts = strings.Split(ptr, ptrIPv6domain)
143
+	} else {
144
+		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
145
+	}
146
+
147
+	host := r.sb.ResolveIP(parts[0])
148
+	if len(host) == 0 {
149
+		return nil, nil
150
+	}
151
+
152
+	log.Debugf("Lookup for IP %s: name %s", parts[0], host)
153
+	fqdn := dns.Fqdn(host)
154
+
155
+	resp := new(dns.Msg)
156
+	resp.SetReply(query)
157
+
158
+	rr := new(dns.PTR)
159
+	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
160
+	rr.Ptr = fqdn
161
+	resp.Answer = append(resp.Answer, rr)
162
+	return resp, nil
163
+}
164
+
165
+func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
166
+	var (
167
+		resp *dns.Msg
168
+		err  error
169
+	)
170
+
171
+	name := query.Question[0].Name
172
+	if query.Question[0].Qtype == dns.TypeA {
173
+		resp, err = r.handleIPv4Query(name, query)
174
+	} else if query.Question[0].Qtype == dns.TypePTR {
175
+		resp, err = r.handlePTRQuery(name, query)
176
+	}
177
+
178
+	if err != nil {
179
+		log.Error(err)
180
+		return
181
+	}
182
+
183
+	if resp == nil {
184
+		if len(r.extDNS) == 0 {
185
+			return
186
+		}
187
+		log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[0], name, query.Question[0].Qtype)
188
+
189
+		c := &dns.Client{Net: "udp"}
190
+		addr := fmt.Sprintf("%s:%d", r.extDNS[0], 53)
191
+
192
+		// TODO: iterate over avilable servers in case of error
193
+		resp, _, err = c.Exchange(query, addr)
194
+		if err != nil {
195
+			log.Errorf("external resolution failed, %s", err)
196
+			return
197
+		}
198
+	}
199
+
200
+	err = w.WriteMsg(resp)
201
+	if err != nil {
202
+		log.Errorf("error writing resolver resp, %s", err)
203
+	}
204
+}
... ...
@@ -5,9 +5,11 @@ import (
5 5
 	"encoding/json"
6 6
 	"fmt"
7 7
 	"io/ioutil"
8
+	"net"
8 9
 	"os"
9 10
 	"path"
10 11
 	"path/filepath"
12
+	"strings"
11 13
 	"sync"
12 14
 
13 15
 	log "github.com/Sirupsen/logrus"
... ...
@@ -38,6 +40,12 @@ type Sandbox interface {
38 38
 	Rename(name string) error
39 39
 	// Delete destroys this container after detaching it from all connected endpoints.
40 40
 	Delete() error
41
+	// ResolveName searches for the service name in the networks to which the sandbox
42
+	// is connected to.
43
+	ResolveName(name string) net.IP
44
+	// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
45
+	// notation; the format used for DNS PTR records
46
+	ResolveIP(name string) string
41 47
 }
42 48
 
43 49
 // SandboxOption is a option setter function type used to pass varios options to
... ...
@@ -59,8 +67,11 @@ type sandbox struct {
59 59
 	id            string
60 60
 	containerID   string
61 61
 	config        containerConfig
62
+	extDNS        []string
62 63
 	osSbox        osl.Sandbox
63 64
 	controller    *controller
65
+	resolver      Resolver
66
+	resolverOnce  sync.Once
64 67
 	refCnt        int
65 68
 	endpoints     epHeap
66 69
 	epPriority    map[string]int
... ...
@@ -202,6 +213,10 @@ func (sb *sandbox) Delete() error {
202 202
 	// likely not required any more. Drop it.
203 203
 	etchosts.Drop(sb.config.hostsPath)
204 204
 
205
+	if sb.resolver != nil {
206
+		sb.resolver.Stop()
207
+	}
208
+
205 209
 	if sb.osSbox != nil && !sb.config.useDefaultSandBox {
206 210
 		sb.osSbox.Destroy()
207 211
 	}
... ...
@@ -291,6 +306,26 @@ func (sb *sandbox) UnmarshalJSON(b []byte) (err error) {
291 291
 	return nil
292 292
 }
293 293
 
294
+func (sb *sandbox) startResolver() {
295
+	sb.resolverOnce.Do(func() {
296
+		var err error
297
+		sb.resolver = NewResolver(sb)
298
+		defer func() {
299
+			if err != nil {
300
+				sb.resolver = nil
301
+			}
302
+		}()
303
+
304
+		sb.rebuildDNS()
305
+		sb.resolver.SetExtServers(sb.extDNS)
306
+
307
+		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
308
+		if err := sb.resolver.Start(); err != nil {
309
+			log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
310
+		}
311
+	})
312
+}
313
+
294 314
 func (sb *sandbox) setupResolutionFiles() error {
295 315
 	if err := sb.buildHostsFile(); err != nil {
296 316
 		return err
... ...
@@ -361,6 +396,56 @@ func (sb *sandbox) updateGateway(ep *endpoint) error {
361 361
 	return nil
362 362
 }
363 363
 
364
+func (sb *sandbox) ResolveIP(ip string) string {
365
+	var svc string
366
+	log.Debugf("IP To resolve %v", ip)
367
+
368
+	for _, ep := range sb.getConnectedEndpoints() {
369
+		n := ep.getNetwork()
370
+
371
+		sr, ok := n.getController().svcDb[n.ID()]
372
+		if !ok {
373
+			continue
374
+		}
375
+
376
+		nwName := n.Name()
377
+		n.Lock()
378
+		svc, ok = sr.ipMap[ip]
379
+		n.Unlock()
380
+		if ok {
381
+			return svc + "." + nwName
382
+		}
383
+	}
384
+	return svc
385
+}
386
+
387
+func (sb *sandbox) ResolveName(name string) net.IP {
388
+	var ip net.IP
389
+	parts := strings.Split(name, ".")
390
+	log.Debugf("To resolve %v", parts)
391
+
392
+	for _, ep := range sb.getConnectedEndpoints() {
393
+		n := ep.getNetwork()
394
+
395
+		if len(parts) > 1 && parts[1] != "" && parts[1] != n.Name() {
396
+			continue
397
+		}
398
+
399
+		sr, ok := n.getController().svcDb[n.ID()]
400
+		if !ok {
401
+			continue
402
+		}
403
+
404
+		n.Lock()
405
+		ip, ok = sr.svcMap[parts[0]]
406
+		n.Unlock()
407
+		if ok {
408
+			return ip
409
+		}
410
+	}
411
+	return ip
412
+}
413
+
364 414
 func (sb *sandbox) SetKey(basePath string) error {
365 415
 	var err error
366 416
 	if basePath == "" {
... ...
@@ -459,6 +544,10 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
459 459
 	i := ep.iface
460 460
 	ep.Unlock()
461 461
 
462
+	if ep.needResolver() {
463
+		sb.startResolver()
464
+	}
465
+
462 466
 	if i != nil && i.srcName != "" {
463 467
 		var ifaceOptions []osl.IfaceOption
464 468
 
... ...
@@ -599,7 +688,7 @@ func (sb *sandbox) buildHostsFile() error {
599 599
 	return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
600 600
 }
601 601
 
602
-func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record) error {
602
+func (sb *sandbox) updateHostsFile(ifaceIP string) error {
603 603
 	var mhost string
604 604
 
605 605
 	if sb.config.originHostsPath != "" {
... ...
@@ -613,11 +702,7 @@ func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record)
613 613
 		mhost = sb.config.hostName
614 614
 	}
615 615
 
616
-	extraContent := make([]etchosts.Record, 0, len(svcRecords)+1)
617
-	extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ifaceIP})
618
-	for _, svc := range svcRecords {
619
-		extraContent = append(extraContent, svc)
620
-	}
616
+	extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
621 617
 
622 618
 	sb.addHostsEntries(extraContent)
623 619
 	return nil
... ...
@@ -787,6 +872,48 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
787 787
 	return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
788 788
 }
789 789
 
790
+// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
791
+// resolv.conf by doing the follwing
792
+// - Save the external name servers in resolv.conf in the sandbox
793
+// - Add only the embedded server's IP to container's resolv.conf
794
+// - If the embedded server needs any resolv.conf options add it to the current list
795
+func (sb *sandbox) rebuildDNS() error {
796
+	currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
797
+	if err != nil {
798
+		return err
799
+	}
800
+
801
+	// localhost entries have already been filtered out from the list
802
+	sb.extDNS = resolvconf.GetNameservers(currRC.Content)
803
+
804
+	var (
805
+		dnsList        = []string{sb.resolver.NameServer()}
806
+		dnsOptionsList = resolvconf.GetOptions(currRC.Content)
807
+		dnsSearchList  = resolvconf.GetSearchDomains(currRC.Content)
808
+	)
809
+
810
+	// Resolver returns the options in the format resolv.conf expects
811
+	dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
812
+
813
+	dir := path.Dir(sb.config.resolvConfPath)
814
+	tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
815
+	if err != nil {
816
+		return err
817
+	}
818
+
819
+	// Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600
820
+	if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil {
821
+		return err
822
+	}
823
+
824
+	_, err = resolvconf.Build(tmpResolvFile.Name(), dnsList, dnsSearchList, dnsOptionsList)
825
+	if err != nil {
826
+		return err
827
+	}
828
+
829
+	return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
830
+}
831
+
790 832
 // joinLeaveStart waits to ensure there are no joins or leaves in progress and
791 833
 // marks this join/leave in progress without race
792 834
 func (sb *sandbox) joinLeaveStart() {
... ...
@@ -179,6 +179,23 @@ function test_single_network_connectivity() {
179 179
 	done
180 180
     done
181 181
 
182
+    svcs=(
183
+	0,0
184
+	2,3
185
+	1,3
186
+	1,2
187
+    )
188
+
189
+    echo "Test connectivity failure"
190
+    for i in `seq ${start} ${end}`;
191
+    do
192
+	IFS=, read a b <<<"${svcs[$i]}"
193
+	osvc="svc${a}${b}"
194
+	echo "pinging ${osvc}"
195
+	runc_nofail $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) "ping -c 1 ${osvc}"
196
+	[ "${status}" -ne 0 ]
197
+    done
198
+
182 199
     for i in `seq ${start} ${end}`;
183 200
     do
184 201
 	for j in `seq ${start} ${end}`;
... ...
@@ -351,84 +351,6 @@ function check_etchosts() {
351 351
     echo ${retval}
352 352
 }
353 353
 
354
-function test_overlay_etchosts() {
355
-    local clist dnet_suffix
356
-
357
-    dnet_suffix=$1
358
-    shift
359
-
360
-    echo $(docker ps)
361
-
362
-    start=1
363
-    end=3
364
-    # Setup overlay network and connect containers ot it
365
-    dnet_cmd $(inst_id2port 1) network create -d overlay multihost
366
-
367
-    for iter in `seq 1 2`;
368
-    do
369
-	for i in `seq ${start} ${end}`;
370
-	do
371
-	    dnet_cmd $(inst_id2port $i) container create container_${iter}_${i}
372
-	    net_connect ${i} container_${iter}_${i} multihost
373
-	done
374
-
375
-	# Now test the /etc/hosts content of all the containers
376
-	for i in `seq ${start} ${end}`;
377
-	do
378
-	    clist=""
379
-	    oldclist=""
380
-	    for j in `seq ${start} ${end}`;
381
-	    do
382
-		if [ "$i" -eq "$j" ]; then
383
-		    continue
384
-		fi
385
-		clist="$clist container_${iter}_$j"
386
-		oldclist="$oldclist container_1_$j"
387
-	    done
388
-	    rv=$(check_etchosts $(dnet_container_name $i $dnet_suffix) \
389
-				$(get_sbox_id ${i} container_${iter}_${i}) \
390
-				${clist})
391
-	    [ "$rv" = "true" ]
392
-
393
-	    # check to see the containers don't have stale entries from previous iteration
394
-	    if [ "$iter" -eq 2 ]; then
395
-		rv=$(check_etchosts $(dnet_container_name $i $dnet_suffix) \
396
-				    $(get_sbox_id ${i} container_${iter}_${i}) \
397
-				    ${oldclist})
398
-		[ "$rv" = "false" ]
399
-	    fi
400
-	done
401
-
402
-	# Teardown the container connections and the network
403
-	clist=""
404
-	for i in `seq ${start} ${end}`;
405
-	do
406
-	    net_disconnect ${i} container_${iter}_${i} multihost
407
-	    dnet_cmd $(inst_id2port $i) container rm container_${iter}_${i}
408
-
409
-	    #check if the /etc/hosts of other containers does not contain this container
410
-	    for j in `seq ${start} ${end}`;
411
-	    do
412
-		if [ "$i" -eq "$j" ]; then
413
-		    continue
414
-		fi
415
-
416
-		if [[ "${clist}" =~ .*container_${iter}_${j}.* ]]; then
417
-		    continue
418
-		fi
419
-
420
-		rv=$(check_etchosts $(dnet_container_name $j $dnet_suffix) \
421
-				    $(get_sbox_id ${j} container_${iter}_${j}) \
422
-				    container_${iter}_${i})
423
-		[ "$rv" = "false" ]
424
-	    done
425
-	    clist="${clist} container_${iter}_${i}"
426
-	done
427
-    done
428
-
429
-    dnet_cmd $(inst_id2port 2) network rm multihost
430
-}
431
-
432 354
 function test_overlay_singlehost() {
433 355
     dnet_suffix=$1
434 356
     shift
... ...
@@ -13,11 +13,6 @@ load helpers
13 13
     test_overlay_singlehost consul
14 14
 }
15 15
 
16
-@test "test overlay network etc hosts with consul" {
17
-    skip_for_circleci
18
-    test_overlay_etchosts consul
19
-}
20
-
21 16
 @test "Test overlay network with dnet restart" {
22 17
     skip_for_circleci
23 18
     test_overlay consul skip_rm
... ...
@@ -33,4 +28,4 @@ load helpers
33 33
 @test "Test overlay network internal network with consul" {
34 34
     skip_for_circleci
35 35
     test_overlay consul internal
36
-}
37 36
\ No newline at end of file
37
+}