DNS support

Signed-off-by: msabansal <sabansal@microsoft.com>
This commit is contained in:
msabansal 2016-09-19 15:48:06 -07:00
parent ae98412893
commit 7f43fd30f3
15 changed files with 396 additions and 141 deletions

View File

@ -804,6 +804,8 @@ func (c *controller) addNetwork(n *network) error {
return err
}
n.startResolver()
return nil
}

View File

@ -15,4 +15,19 @@ const (
// QosPolicies of the endpoint
QosPolicies = "com.docker.endpoint.windowsshim.qospolicies"
// VLAN of the network
VLAN = "com.docker.network.windowsshim.vlanid"
// VSID of the network
VSID = "com.docker.network.windowsshim.vsid"
// DNSSuffix of the network
DNSSuffix = "com.docker.network.windowsshim.dnssuffix"
// DNSServers of the network
DNSServers = "com.docker.network.windowsshim.dnsservers"
// SourceMac of the network
SourceMac = "com.docker.network.windowsshim.sourcemac"
)

View File

@ -15,6 +15,7 @@ import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"
"sync"
@ -34,6 +35,11 @@ type networkConfiguration struct {
Name string
HnsID string
RDID string
VLAN uint
VSID uint
DNSServers string
DNSSuffix string
SourceMac string
NetworkAdapterName string
}
@ -43,6 +49,7 @@ type endpointConfiguration struct {
PortBindings []types.PortBinding
ExposedPorts []types.TransportPort
QosPolicies []types.QosPolicy
DNSServers []string
}
type hnsEndpoint struct {
@ -69,7 +76,7 @@ type driver struct {
}
func isValidNetworkType(networkType string) bool {
if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "transparent" == networkType {
if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType {
return true
}
@ -129,6 +136,22 @@ func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string
config.RDID = value
case Interface:
config.NetworkAdapterName = value
case DNSSuffix:
config.DNSSuffix = value
case DNSServers:
config.DNSServers = value
case VLAN:
vlan, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return nil, err
}
config.VLAN = uint(vlan)
case VSID:
vsid, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return nil, err
}
config.VSID = uint(vsid)
}
}
@ -207,9 +230,36 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
Name: config.Name,
Type: d.name,
Subnets: subnets,
DNSServerList: config.DNSServers,
DNSSuffix: config.DNSSuffix,
SourceMac: config.SourceMac,
NetworkAdapterName: config.NetworkAdapterName,
}
if config.VLAN != 0 {
vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{
Type: "VLAN",
VLAN: config.VLAN,
})
if err != nil {
return err
}
network.Policies = append(network.Policies, vlanPolicy)
}
if config.VSID != 0 {
vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{
Type: "VSID",
VSID: config.VSID,
})
if err != nil {
return err
}
network.Policies = append(network.Policies, vsidPolicy)
}
if network.Name == "" {
network.Name = id
}
@ -379,6 +429,14 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfigurat
}
}
if opt, ok := epOptions[netlabel.DNSServers]; ok {
if dns, ok := opt.([]string); ok {
ec.DNSServers = dns
} else {
return nil, fmt.Errorf("Invalid endpoint configuration")
}
}
return ec, nil
}
@ -421,6 +479,12 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
endpointStruct.IPAddress = ifInfo.Address().IP
}
endpointStruct.DNSServerList = strings.Join(ec.DNSServers, ",")
if n.driver.name == "nat" {
endpointStruct.EnableInternalDNS = true
}
configurationb, err := json.Marshal(endpointStruct)
if err != nil {
return err
@ -502,6 +566,10 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro
}
data := make(map[string]interface{}, 1)
if network.driver.name == "nat" {
data["AllowUnqualifiedDNSQuery"] = true
}
data["hnsid"] = ep.profileID
if ep.config.ExposedPorts != nil {
// Return a copy of the config data

View File

@ -29,7 +29,7 @@ func testNetwork(networkType string, t *testing.T) {
},
}
err := d.CreateNetwork("dummy", netOption, ipdList, nil)
err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil)
if err != nil {
t.Fatalf("Failed to create bridge: %v", err)
}

View File

@ -883,6 +883,14 @@ func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
}
}
// CreateOptionDNS function returns an option setter for dns entry option to
// be passed to container Create method.
func CreateOptionDNS(dns []string) EndpointOption {
return func(ep *endpoint) {
ep.generic[netlabel.DNSServers] = dns
}
}
// CreateOptionAnonymous function returns an option setter for setting
// this endpoint as anonymous
func CreateOptionAnonymous() EndpointOption {

View File

@ -389,10 +389,8 @@ func TestSRVServiceQuery(t *testing.T) {
c.(*controller).svcRecords[n.ID()] = sr
_, ip, err := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm")
if err != nil {
t.Fatal(err)
}
_, ip := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm")
if len(ip) == 0 {
t.Fatal(err)
}
@ -400,10 +398,8 @@ func TestSRVServiceQuery(t *testing.T) {
t.Fatal(err)
}
_, ip, err = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm")
if err != nil {
t.Fatal(err)
}
_, ip = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm")
if len(ip) == 0 {
t.Fatal(err)
}
@ -412,7 +408,7 @@ func TestSRVServiceQuery(t *testing.T) {
}
// Service name with invalid protocol name. Should fail without error
_, ip, err = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm")
_, ip = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm")
if len(ip) != 0 {
t.Fatal("Valid response for invalid service name")
}

View File

@ -1208,8 +1208,8 @@ func (f *fakeSandbox) ResolveIP(ip string) string {
return ""
}
func (f *fakeSandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
return nil, nil, nil
func (f *fakeSandbox) ResolveService(name string) ([]*net.SRV, []net.IP) {
return nil, nil
}
func (f *fakeSandbox) Endpoints() []libnetwork.Endpoint {

View File

@ -27,6 +27,9 @@ const (
// ExposedPorts constant represents the container's Exposed Ports
ExposedPorts = Prefix + ".endpoint.exposedports"
// DNSServers A list of DNS servers associated with the endpoint
DNSServers = Prefix + ".endpoint.dnsservers"
//EnableIPv6 constant represents enabling IPV6 at network level
EnableIPv6 = Prefix + ".enable_ipv6"

View File

@ -184,6 +184,8 @@ type network struct {
persist bool
stopWatchCh chan struct{}
drvOnce *sync.Once
resolverOnce sync.Once
resolver []Resolver
internal bool
inDelete bool
ingress bool
@ -803,6 +805,9 @@ func (n *network) deleteNetwork() error {
}
}
for _, resolver := range n.resolver {
resolver.Stop()
}
return nil
}
@ -1528,3 +1533,126 @@ func (n *network) TableEventRegister(tableName string) error {
func (n *network) hasSpecialDriver() bool {
return n.Type() == "host" || n.Type() == "null"
}
func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
var ipv6Miss bool
c := n.getController()
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
if !ok {
return nil, false
}
req = strings.TrimSuffix(req, ".")
var ip []net.IP
n.Lock()
ip, ok = sr.svcMap[req]
if ipType == types.IPv6 {
// If the name resolved to v4 address then its a valid name in
// the docker network domain. If the network is not v6 enabled
// set ipv6Miss to filter the DNS query from going to external
// resolvers.
if ok && n.enableIPv6 == false {
ipv6Miss = true
}
ip = sr.svcIPv6Map[req]
}
n.Unlock()
if ip != nil {
return ip, false
}
return nil, ipv6Miss
}
func (n *network) ResolveIP(ip string) string {
var svc string
c := n.getController()
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
if !ok {
return ""
}
nwName := n.Name()
n.Lock()
defer n.Unlock()
svc, ok = sr.ipMap[ip]
if ok {
return svc + "." + nwName
}
return svc
}
func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) {
c := n.getController()
srv := []*net.SRV{}
ip := []net.IP{}
log.Debugf("Service name To resolve: %v", name)
// There are DNS implementaions that allow SRV queries for names not in
// the format defined by RFC 2782. Hence specific validations checks are
// not done
parts := strings.Split(name, ".")
if len(parts) < 3 {
return nil, nil
}
portName := parts[0]
proto := parts[1]
svcName := strings.Join(parts[2:], ".")
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
if !ok {
return nil, nil
}
svcs, ok := sr.service[svcName]
if !ok {
return nil, nil
}
for _, svc := range svcs {
if svc.portName != portName {
continue
}
if svc.proto != proto {
continue
}
for _, t := range svc.target {
srv = append(srv,
&net.SRV{
Target: t.name,
Port: t.port,
})
ip = append(ip, t.ip)
}
}
return srv, ip
}
func (n *network) ExecFunc(f func()) error {
return types.NotImplementedErrorf("ExecFunc not supported by network")
}
func (n *network) NdotsSet() bool {
return false
}

View File

@ -0,0 +1,8 @@
// +build !windows
package libnetwork
// Stub implementations for DNS related functions
func (n *network) startResolver() {
}

View File

@ -0,0 +1,52 @@
// +build windows
package libnetwork
import (
"runtime"
"github.com/Microsoft/hcsshim"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/drivers/windows"
)
func executeInCompartment(compartmentID uint32, x func()) {
runtime.LockOSThread()
if err := hcsshim.SetCurrentThreadCompartmentId(compartmentID); err != nil {
log.Error(err)
}
defer func() {
hcsshim.SetCurrentThreadCompartmentId(0)
runtime.UnlockOSThread()
}()
x()
}
func (n *network) startResolver() {
n.resolverOnce.Do(func() {
log.Debugf("Launching DNS server for network", n.Name())
options := n.Info().DriverOptions()
hnsid := options[windows.HNSID]
hnsresponse, err := hcsshim.HNSNetworkRequest("GET", hnsid, "")
if err != nil {
log.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err)
return
}
for _, subnet := range hnsresponse.Subnets {
if subnet.GatewayAddress != "" {
resolver := NewResolver(subnet.GatewayAddress, false, "", n)
log.Debugf("Binding a resolver on network %s gateway %s", n.Name(), subnet.GatewayAddress)
executeInCompartment(hnsresponse.DNSServerCompartment, resolver.SetupFunc(53))
if err = resolver.Start(); err != nil {
log.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err)
} else {
n.resolver = append(n.resolver, resolver)
}
}
}
})
}

View File

@ -23,7 +23,7 @@ type Resolver interface {
Stop()
// SetupFunc() provides the setup function that should be run
// in the container's network namespace.
SetupFunc() func()
SetupFunc(int) func()
// NameServer() returns the IP of the DNS resolver for the
// containers.
NameServer() string
@ -34,8 +34,29 @@ type Resolver interface {
ResolverOptions() []string
}
// DNSBackend represents a backend DNS resolver used for DNS name
// resolution. All the queries to the resolver are forwared to the
// backend resolver.
type DNSBackend interface {
// ResolveName resolves a service name to an IPv4 or IPv6 address by searching
// the networks the sandbox is connected to. For IPv6 queries, second return
// value will be true if the name exists in docker domain but doesn't have an
// IPv6 address. Such queries shouldn't be forwarded to external nameservers.
ResolveName(name string, iplen int) ([]net.IP, bool)
// 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
// ResolveService returns all the backend details about the containers or hosts
// backing a service. Its purpose is to satisfy an SRV query
ResolveService(name string) ([]*net.SRV, []net.IP)
// ExecFunc allows a function to be executed in the context of the backend
// on behalf of the resolver.
ExecFunc(f func()) error
//NdotsSet queries the backends ndots dns option settings
NdotsSet() bool
}
const (
resolverIP = "127.0.0.11"
dnsPort = "53"
ptrIPv4domain = ".in-addr.arpa."
ptrIPv6domain = ".ip6.arpa."
@ -53,16 +74,19 @@ type extDNSEntry struct {
// resolver implements the Resolver interface
type resolver struct {
sb *sandbox
extDNSList [maxExtDNS]extDNSEntry
server *dns.Server
conn *net.UDPConn
tcpServer *dns.Server
tcpListen *net.TCPListener
err error
count int32
tStamp time.Time
queryLock sync.Mutex
backend DNSBackend
extDNSList [maxExtDNS]extDNSEntry
server *dns.Server
conn *net.UDPConn
tcpServer *dns.Server
tcpListen *net.TCPListener
err error
count int32
tStamp time.Time
queryLock sync.Mutex
listenAddress string
proxyDNS bool
resolverKey string
}
func init() {
@ -70,20 +94,24 @@ func init() {
}
// NewResolver creates a new instance of the Resolver
func NewResolver(sb *sandbox) Resolver {
func NewResolver(address string, proxyDNS bool, resolverKey string, backend DNSBackend) Resolver {
return &resolver{
sb: sb,
err: fmt.Errorf("setup not done yet"),
backend: backend,
proxyDNS: proxyDNS,
listenAddress: address,
resolverKey: resolverKey,
err: fmt.Errorf("setup not done yet"),
}
}
func (r *resolver) SetupFunc() func() {
func (r *resolver) SetupFunc(port int) func() {
return (func() {
var err error
// DNS operates primarily on UDP
addr := &net.UDPAddr{
IP: net.ParseIP(resolverIP),
IP: net.ParseIP(r.listenAddress),
Port: port,
}
r.conn, err = net.ListenUDP("udp", addr)
@ -94,7 +122,8 @@ func (r *resolver) SetupFunc() func() {
// Listen on a TCP as well
tcpaddr := &net.TCPAddr{
IP: net.ParseIP(resolverIP),
IP: net.ParseIP(r.listenAddress),
Port: port,
}
r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
@ -156,7 +185,7 @@ func (r *resolver) SetExtServers(dns []string) {
}
func (r *resolver) NameServer() string {
return resolverIP
return r.listenAddress
}
func (r *resolver) ResolverOptions() []string {
@ -184,7 +213,10 @@ func createRespMsg(query *dns.Msg) *dns.Msg {
}
func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) {
addr, ipv6Miss := r.sb.ResolveName(name, ipType)
var addr []net.IP
var ipv6Miss bool
addr, ipv6Miss = r.backend.ResolveName(name, ipType)
if addr == nil && ipv6Miss {
// Send a reply without any Answer sections
log.Debugf("Lookup name %s present without IPv6 address", name)
@ -230,7 +262,8 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
return nil, fmt.Errorf("invalid PTR query, %v", ptr)
}
host := r.sb.ResolveIP(parts[0])
host := r.backend.ResolveIP(parts[0])
if len(host) == 0 {
return nil, nil
}
@ -250,11 +283,9 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
}
func (r *resolver) handleSRVQuery(svc string, query *dns.Msg) (*dns.Msg, error) {
srv, ip, err := r.sb.ResolveService(svc)
if err != nil {
return nil, err
}
srv, ip := r.backend.ResolveService(svc)
if len(srv) == 0 {
return nil, nil
}
@ -325,16 +356,25 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
return
}
// If the user sets ndots > 0 explicitly and the query is
// in the root domain don't forward it out. We will return
// failure and let the client retry with the search domain
// attached
if resp == nil {
// If the backend doesn't support proxying dns request
// fail the response
if !r.proxyDNS {
resp = new(dns.Msg)
resp.SetRcode(query, dns.RcodeServerFailure)
w.WriteMsg(resp)
return
}
// If the user sets ndots > 0 explicitly and the query is
// in the root domain don't forward it out. We will return
// failure and let the client retry with the search domain
// attached
switch query.Question[0].Qtype {
case dns.TypeA:
fallthrough
case dns.TypeAAAA:
if r.sb.ndotsSet && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
resp = createRespMsg(query)
}
}
@ -369,8 +409,8 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
}
r.sb.execFunc(extConnect)
if err != nil {
execErr := r.backend.ExecFunc(extConnect)
if execErr != nil || err != nil {
log.Debugf("Connect failed, %s", err)
continue
}

View File

@ -35,7 +35,7 @@ func reexecSetupResolver() {
os.Exit(1)
}
_, ipPort, _ := net.SplitHostPort(os.Args[2])
resolverIP, ipPort, _ := net.SplitHostPort(os.Args[2])
_, tcpPort, _ := net.SplitHostPort(os.Args[3])
rules := [][]string{
{"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[2]},
@ -90,7 +90,7 @@ func (r *resolver) setupIPTable() error {
cmd := &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{"setup-resolver"}, r.sb.Key(), laddr, ltcpaddr),
Args: append([]string{"setup-resolver"}, r.resolverKey, laddr, ltcpaddr),
Stdout: os.Stdout,
Stderr: os.Stderr,
}

View File

@ -37,19 +37,11 @@ type Sandbox interface {
Rename(name string) error
// Delete destroys this container after detaching it from all connected endpoints.
Delete() error
// ResolveName resolves a service name to an IPv4 or IPv6 address by searching
// the networks the sandbox is connected to. For IPv6 queries, second return
// value will be true if the name exists in docker domain but doesn't have an
// IPv6 address. Such queries shouldn't be forwarded to external nameservers.
ResolveName(name string, iplen int) ([]net.IP, bool)
// 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
// ResolveService returns all the backend details about the containers or hosts
// backing a service. Its purpose is to satisfy an SRV query
ResolveService(name string) ([]*net.SRV, []net.IP, error)
// Endpoints returns all the endpoints connected to the sandbox
Endpoints() []Endpoint
// ResolveService returns all the backend details about the containers or hosts
// backing a service. Its purpose is to satisfy an SRV query
ResolveService(name string) ([]*net.SRV, []net.IP)
}
// SandboxOption is an option setter function type used to pass various options to
@ -131,6 +123,10 @@ type containerConfig struct {
exposedPorts []types.TransportPort
}
const (
resolverIPSandbox = "127.0.0.11"
)
func (sb *sandbox) ID() string {
return sb.id
}
@ -415,33 +411,21 @@ func (sb *sandbox) ResolveIP(ip string) string {
for _, ep := range sb.getConnectedEndpoints() {
n := ep.getNetwork()
c := n.getController()
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
if !ok {
continue
}
nwName := n.Name()
n.Lock()
svc, ok = sr.ipMap[ip]
n.Unlock()
if ok {
return svc + "." + nwName
svc = n.ResolveIP(ip)
if len(svc) != 0 {
return svc
}
}
return svc
}
func (sb *sandbox) execFunc(f func()) {
func (sb *sandbox) ExecFunc(f func()) error {
sb.osSbox.InvokeFunc(f)
return nil
}
func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP) {
srv := []*net.SRV{}
ip := []net.IP{}
@ -452,53 +436,18 @@ func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
// not done
parts := strings.Split(name, ".")
if len(parts) < 3 {
return nil, nil, nil
return nil, nil
}
portName := parts[0]
proto := parts[1]
svcName := strings.Join(parts[2:], ".")
for _, ep := range sb.getConnectedEndpoints() {
n := ep.getNetwork()
c := n.getController()
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
if !ok {
continue
}
svcs, ok := sr.service[svcName]
if !ok {
continue
}
for _, svc := range svcs {
if svc.portName != portName {
continue
}
if svc.proto != proto {
continue
}
for _, t := range svc.target {
srv = append(srv,
&net.SRV{
Target: t.name,
Port: t.port,
})
ip = append(ip, t.ip)
}
}
srv, ip = n.ResolveService(name)
if len(srv) > 0 {
break
}
}
return srv, ip, nil
return srv, ip
}
func getDynamicNwEndpoints(epList []*endpoint) []*endpoint {
@ -635,33 +584,15 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
ep.Unlock()
}
c := n.getController()
c.Lock()
sr, ok := c.svcRecords[n.ID()]
c.Unlock()
ip, miss := n.ResolveName(name, ipType)
if !ok {
continue
}
var ip []net.IP
n.Lock()
ip, ok = sr.svcMap[name]
if ipType == types.IPv6 {
// If the name resolved to v4 address then its a valid name in
// the docker network domain. If the network is not v6 enabled
// set ipv6Miss to filter the DNS query from going to external
// resolvers.
if ok && n.enableIPv6 == false {
ipv6Miss = true
}
ip = sr.svcIPv6Map[name]
}
n.Unlock()
if ip != nil {
return ip, false
}
if miss {
ipv6Miss = miss
}
}
return nil, ipv6Miss
}
@ -708,7 +639,7 @@ func (sb *sandbox) SetKey(basePath string) error {
if oldosSbox != nil && sb.resolver != nil {
sb.resolver.Stop()
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0))
if err := sb.resolver.Start(); err != nil {
log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
}
@ -1231,3 +1162,7 @@ func (eh *epHeap) Pop() interface{} {
*eh = old[0 : n-1]
return x
}
func (sb *sandbox) NdotsSet() bool {
return sb.ndotsSet
}

View File

@ -26,7 +26,7 @@ const (
func (sb *sandbox) startResolver(restore bool) {
sb.resolverOnce.Do(func() {
var err error
sb.resolver = NewResolver(sb)
sb.resolver = NewResolver(resolverIPSandbox, true, sb.Key(), sb)
defer func() {
if err != nil {
sb.resolver = nil
@ -46,7 +46,7 @@ func (sb *sandbox) startResolver(restore bool) {
}
sb.resolver.SetExtServers(sb.extDNS)
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0))
if err = sb.resolver.Start(); err != nil {
log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
}