diff --git a/nameserver/cache.go b/nameserver/cache.go
index 624236b8..32451725 100644
--- a/nameserver/cache.go
+++ b/nameserver/cache.go
@@ -22,6 +22,8 @@ var (
)
const (
+ DefaultCacheNegLocalTTL = 30 // Default TTL for negative local resolutions
+
defPendingTimeout int = 5 // timeout for a resolution
)
diff --git a/nameserver/cache_test.go b/nameserver/cache_test.go
index 890bea9b..47830dfd 100644
--- a/nameserver/cache_test.go
+++ b/nameserver/cache_test.go
@@ -36,7 +36,7 @@ func TestCacheLength(t *testing.T) {
ip := net.ParseIP(fmt.Sprintf("10.0.1.%d", i))
records := []ZoneRecord{Record{name, ip, 0, 0, 0}}
- reply := makeAddressReply(questionMsg, question, records)
+ reply := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
reply.Answer[0].Header().Ttl = uint32(i)
l.Put(questionMsg, reply, 0, 0)
@@ -89,7 +89,7 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Inserting the reply")
records := []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.1"), 0, 0, 0}}
- reply1 := makeAddressReply(questionMsg, question, records)
+ reply1 := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
l.Put(questionMsg, reply1, nullTTL, 0)
t.Logf("Checking we can Get() the reply now")
@@ -110,8 +110,8 @@ func TestCacheEntries(t *testing.T) {
ttlGet2 := resp.Answer[0].Header().Ttl
wt.AssertEqualInt(t, int(ttlGet1-ttlGet2), 1, "TTL difference")
- clk.Add(time.Duration(localTTL) * time.Second)
- t.Logf("Checking that a third Get(), after %d second, gets no result", localTTL)
+ clk.Add(time.Duration(DefaultLocalTTL) * time.Second)
+ t.Logf("Checking that a third Get(), after %d second, gets no result", DefaultLocalTTL)
resp, err = l.Get(questionMsg, minUDPSize)
wt.AssertNoErr(t, err)
if resp != nil {
@@ -121,7 +121,7 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Checking that an Remove() results in Get() returning nothing")
records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.9.9"), 0, 0, 0}}
- replyTemp := makeAddressReply(questionMsg, question, records)
+ replyTemp := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
l.Put(questionMsg, replyTemp, nullTTL, 0)
lenBefore := l.Len()
l.Remove(question)
@@ -135,11 +135,11 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Inserting a two replies for the same query")
records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.2"), 0, 0, 0}}
- reply2 := makeAddressReply(questionMsg, question, records)
+ reply2 := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
l.Put(questionMsg, reply2, nullTTL, 0)
clk.Add(time.Duration(1) * time.Second)
records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.3"), 0, 0, 0}}
- reply3 := makeAddressReply(questionMsg, question, records)
+ reply3 := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
l.Put(questionMsg, reply3, nullTTL, 0)
t.Logf("Checking we get the last one...")
@@ -149,9 +149,9 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Received '%s'", resp.Answer[0])
wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address")
- wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), int(localTTL), "TTL")
+ wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), int(DefaultLocalTTL), "TTL")
- clk.Add(time.Duration(localTTL-1) * time.Second)
+ clk.Add(time.Duration(DefaultLocalTTL-1) * time.Second)
resp, err = l.Get(questionMsg, minUDPSize)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp != nil, "reponse from the Get()")
@@ -162,7 +162,7 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Checking we get empty replies when they are expired...")
lenBefore = l.Len()
- clk.Add(time.Duration(localTTL) * time.Second)
+ clk.Add(time.Duration(DefaultLocalTTL) * time.Second)
resp, err = l.Get(questionMsg, minUDPSize)
wt.AssertNoErr(t, err)
if resp != nil {
@@ -183,7 +183,7 @@ func TestCacheEntries(t *testing.T) {
t.Logf("Checking that an Remove() between Get() and Put() does not break things")
records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.9.9"), 0, 0, 0}}
- replyTemp2 := makeAddressReply(questionMsg2, question2, records)
+ replyTemp2 := makeAddressReply(questionMsg2, question2, records, DefaultLocalTTL)
l.Remove(question2)
l.Put(questionMsg2, replyTemp2, nullTTL, 0)
resp, err = l.Get(questionMsg2, minUDPSize)
@@ -201,8 +201,8 @@ func TestCacheEntries(t *testing.T) {
wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies")
wt.AssertNotNil(t, err, "Get() error with CacheNoLocalReplies")
- clk.Add(time.Second * time.Duration(negLocalTTL+1))
- t.Logf("Checking that we get an expired response after %d seconds", negLocalTTL)
+ clk.Add(time.Second * time.Duration(DefaultCacheNegLocalTTL+1))
+ t.Logf("Checking that we get an expired response after %d seconds", DefaultCacheNegLocalTTL)
resp, err = l.Get(questionMsg3, minUDPSize)
wt.AssertNil(t, resp, "expired Get() response with CacheNoLocalReplies")
wt.AssertNil(t, err, "expired Get() error with CacheNoLocalReplies")
diff --git a/nameserver/dns.go b/nameserver/dns.go
index 8c08a2db..c232d45b 100644
--- a/nameserver/dns.go
+++ b/nameserver/dns.go
@@ -8,16 +8,17 @@ import (
)
const (
- localTTL uint32 = 30 // somewhat arbitrary; we don't expect anyone downstream to cache results
- negLocalTTL = 30 // TTL for negative local resolutions
- minUDPSize = 512
- maxUDPSize = 65535
+ minUDPSize = 512
+ maxUDPSize = 65535
)
-func makeHeader(r *dns.Msg, q *dns.Question) *dns.RR_Header {
+func makeHeader(r *dns.Msg, q *dns.Question, ttl int) *dns.RR_Header {
return &dns.RR_Header{
- Name: q.Name, Rrtype: q.Qtype,
- Class: dns.ClassINET, Ttl: localTTL}
+ Name: q.Name,
+ Rrtype: q.Qtype,
+ Class: dns.ClassINET,
+ Ttl: uint32(ttl),
+ }
}
func makeReply(r *dns.Msg, as []dns.RR) *dns.Msg {
@@ -36,11 +37,11 @@ func makeTruncatedReply(r *dns.Msg) *dns.Msg {
return reply
}
-type DNSResponseBuilder func(r *dns.Msg, q *dns.Question, addrs []ZoneRecord) *dns.Msg
+type DNSResponseBuilder func(r *dns.Msg, q *dns.Question, addrs []ZoneRecord, ttl int) *dns.Msg
-func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []ZoneRecord) *dns.Msg {
+func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []ZoneRecord, ttl int) *dns.Msg {
answers := make([]dns.RR, len(addrs))
- header := makeHeader(r, q)
+ header := makeHeader(r, q, ttl)
count := 0
for _, addr := range addrs {
ip := addr.IP()
@@ -62,9 +63,9 @@ func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []ZoneRecord) *dns.Msg
return makeReply(r, answers[:count])
}
-func makePTRReply(r *dns.Msg, q *dns.Question, names []ZoneRecord) *dns.Msg {
+func makePTRReply(r *dns.Msg, q *dns.Question, names []ZoneRecord, ttl int) *dns.Msg {
answers := make([]dns.RR, len(names))
- header := makeHeader(r, q)
+ header := makeHeader(r, q, ttl)
for i, name := range names {
answers[i] = &dns.PTR{Hdr: *header, Ptr: name.Name()}
}
diff --git a/nameserver/dns_test.go b/nameserver/dns_test.go
index 66eba2cb..21fd22c5 100644
--- a/nameserver/dns_test.go
+++ b/nameserver/dns_test.go
@@ -25,8 +25,8 @@ func TestPrune(t *testing.T) {
Record{"name", net.ParseIP("10.0.1.4"), 0, 0, 0},
}
- reply := makeAddressReply(questionMsg, question, records)
- reply.Answer[0].Header().Ttl = localTTL
+ reply := makeAddressReply(questionMsg, question, records, DefaultLocalTTL)
+ reply.Answer[0].Header().Ttl = DefaultLocalTTL
pruned := pruneAnswers(reply.Answer, 1)
wt.AssertEqualInt(t, len(pruned), 1, "wrong number of answers")
diff --git a/nameserver/mdns_server.go b/nameserver/mdns_server.go
index 482d9244..d95a6c1b 100644
--- a/nameserver/mdns_server.go
+++ b/nameserver/mdns_server.go
@@ -13,13 +13,18 @@ type MDNSServer struct {
srv *dns.Server
zone Zone
allowLocal bool
+ ttl int
running bool
}
// Create a new mDNS server
// Nothing will be done (including port bindings) until you `Start()` the server
-func NewMDNSServer(zone Zone, local bool) (*MDNSServer, error) {
- return &MDNSServer{zone: zone, allowLocal: local}, nil
+func NewMDNSServer(zone Zone, local bool, ttl int) (*MDNSServer, error) {
+ return &MDNSServer{
+ zone: zone,
+ allowLocal: local,
+ ttl: ttl,
+ }, nil
}
// Return true if testaddr is a UDP address with IP matching my local i/f
@@ -66,7 +71,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) (err error) {
handleLocal := s.makeHandler(dns.TypeA,
func(zone ZoneLookup, r *dns.Msg, q *dns.Question) *dns.Msg {
if ips, err := zone.LookupName(q.Name); err == nil {
- return makeAddressReply(r, q, ips)
+ return makeAddressReply(r, q, ips, s.ttl)
}
return nil
})
@@ -74,7 +79,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) (err error) {
handleReverse := s.makeHandler(dns.TypePTR,
func(zone ZoneLookup, r *dns.Msg, q *dns.Question) *dns.Msg {
if names, err := zone.LookupInaddr(q.Name); err == nil {
- return makePTRReply(r, q, names)
+ return makePTRReply(r, q, names, s.ttl)
}
return nil
})
diff --git a/nameserver/mdns_server_test.go b/nameserver/mdns_server_test.go
index 35ece389..bd6723a8 100644
--- a/nameserver/mdns_server_test.go
+++ b/nameserver/mdns_server_test.go
@@ -21,7 +21,7 @@ func TestServerSimpleQuery(t *testing.T) {
Info.Println("TestServerSimpleQuery starting")
mzone := newMockedZoneWithRecords([]ZoneRecord{testRecord1, testRecord2})
- mdnsServer, err := NewMDNSServer(mzone, true)
+ mdnsServer, err := NewMDNSServer(mzone, true, DefaultLocalTTL)
wt.AssertNoErr(t, err)
err = mdnsServer.Start(nil)
wt.AssertNoErr(t, err)
diff --git a/nameserver/mdns_test.go b/nameserver/mdns_test.go
index 2baeb3ff..3127d3c4 100644
--- a/nameserver/mdns_test.go
+++ b/nameserver/mdns_test.go
@@ -18,7 +18,7 @@ func TestClientServerSimpleQuery(t *testing.T) {
testInAddr1 := "1.2.2.10.in-addr.arpa."
mzone := newMockedZoneWithRecords([]ZoneRecord{testRecord1})
- mdnsServer, err := NewMDNSServer(mzone, true)
+ mdnsServer, err := NewMDNSServer(mzone, true, DefaultLocalTTL)
wt.AssertNoErr(t, err)
err = mdnsServer.Start(nil)
wt.AssertNoErr(t, err)
@@ -94,21 +94,21 @@ func TestClientServerInsistentQuery(t *testing.T) {
testInAddr2 := "2.2.2.10.in-addr.arpa."
mzone1 := newMockedZoneWithRecords([]ZoneRecord{testRecord1})
- mdnsServer1, err := NewMDNSServer(mzone1, true)
+ mdnsServer1, err := NewMDNSServer(mzone1, true, DefaultLocalTTL)
wt.AssertNoErr(t, err)
err = mdnsServer1.Start(nil)
wt.AssertNoErr(t, err)
defer mdnsServer1.Stop()
mzone2 := newMockedZoneWithRecords([]ZoneRecord{testRecord2})
- mdnsServer2, err := NewMDNSServer(mzone2, true)
+ mdnsServer2, err := NewMDNSServer(mzone2, true, DefaultLocalTTL)
wt.AssertNoErr(t, err)
err = mdnsServer2.Start(nil)
wt.AssertNoErr(t, err)
defer mdnsServer2.Stop()
// create a third server with exactly the same info as the second server (so we can test duplicates removals)
- mdnsServer3, err := NewMDNSServer(mzone2, true)
+ mdnsServer3, err := NewMDNSServer(mzone2, true, DefaultLocalTTL)
wt.AssertNoErr(t, err)
err = mdnsServer3.Start(nil)
wt.AssertNoErr(t, err)
diff --git a/nameserver/server.go b/nameserver/server.go
index 224568ae..598e8171 100644
--- a/nameserver/server.go
+++ b/nameserver/server.go
@@ -16,6 +16,7 @@ import (
const (
DefaultServerPort = 53 // The default server port
DefaultCLICfgFile = "/etc/resolv.conf" // default "resolv.conf" file to try to load
+ DefaultLocalTTL = 30 // default TTL for responses for local domain queries
DefaultUDPBuflen = 4096 // bigger than the default 512
DefaultCacheLen = 8192 // default cache capacity
DefaultTimeout = 5000 // default timeout for DNS resolutions (millisecs)
@@ -41,6 +42,8 @@ type DNSServerConfig struct {
UDPBufLen int
// (Optional) force a specific cache
Cache ZoneCache
+ // (Optional) TTL for local domain responses
+ LocalTTL int
// (Optional) TTL for negative results in the local domain
CacheNegLocalTTL int
// (Optional) for a specific clock provider
@@ -94,6 +97,7 @@ type DNSServer struct {
cache ZoneCache
cacheDisabled bool
maxAnswers int
+ localTTL int
negLocalTTL int
timeout time.Duration
readTimeout time.Duration
@@ -114,7 +118,8 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
readTimeout: DefaultTimeout * time.Millisecond,
cacheDisabled: false,
maxAnswers: DefaultMaxAnswers,
- negLocalTTL: negLocalTTL,
+ localTTL: DefaultLocalTTL,
+ negLocalTTL: DefaultCacheNegLocalTTL,
clock: config.Clock,
}
@@ -153,6 +158,9 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
if config.MaxAnswers > 0 {
s.maxAnswers = config.MaxAnswers
}
+ if config.LocalTTL > 0 {
+ s.localTTL = config.LocalTTL
+ }
if config.CacheNegLocalTTL > 0 {
s.negLocalTTL = config.CacheNegLocalTTL
}
@@ -344,7 +352,7 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
maybeCache(nil, s.negLocalTTL, CacheNoLocalReplies)
fallback(w, r)
} else {
- m := msgBuilder(r, &q, answers)
+ m := msgBuilder(r, &q, answers, s.localTTL)
m.Authoritative = true
maybeCache(m, nullTTL, 0)
m.Answer = pruneAnswers(shuffleAnswers(m.Answer), s.maxAnswers)
diff --git a/nameserver/server_cache_test.go b/nameserver/server_cache_test.go
index 3e3be2d9..74c922cb 100644
--- a/nameserver/server_cache_test.go
+++ b/nameserver/server_cache_test.go
@@ -17,7 +17,7 @@ func TestServerCacheRefresh(t *testing.T) {
containerID = "somecontainer"
testName1 = "first.weave.local."
testName2 = "second.weave.local."
- refreshInterval = int(localTTL) / 3
+ refreshInterval = int(DefaultLocalTTL) / 3
)
InitDefaultLogging(testing.Verbose())
diff --git a/nameserver/server_test.go b/nameserver/server_test.go
index 6330a52e..1af4c126 100644
--- a/nameserver/server_test.go
+++ b/nameserver/server_test.go
@@ -158,7 +158,7 @@ func TestTCPDNSServer(t *testing.T) {
t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
- m := makeAddressReply(req, &q, addrs)
+ m := makeAddressReply(req, &q, addrs, DefaultLocalTTL)
mLen := m.Len()
m.SetEdns0(uint16(maxLen), false)
@@ -171,7 +171,7 @@ func TestTCPDNSServer(t *testing.T) {
fallbackTCPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
t.Logf("Fallback TCP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
- m := makeAddressReply(req, &q, addrs)
+ m := makeAddressReply(req, &q, addrs, DefaultLocalTTL)
w.WriteMsg(m)
}
diff --git a/nameserver/zone.go b/nameserver/zone.go
index 4fa921dc..4ad7cd06 100644
--- a/nameserver/zone.go
+++ b/nameserver/zone.go
@@ -17,10 +17,10 @@ const (
)
const (
- DefaultLocalDomain = "weave.local." // The default name used for the local domain
- DefaultRefreshInterval = int(localTTL) // Period for background updates with mDNS
- DefaultRelevantTime = 60 // When to forget info about remote info if nobody asks...
- DefaultNumUpdaters = 4 // Default number of background updaters
+ DefaultLocalDomain = "weave.local." // The default name used for the local domain
+ DefaultRefreshInterval = int(DefaultLocalTTL) // Period for background updates with mDNS
+ DefaultRelevantTime = 60 // When to forget info about remote info if nobody asks...
+ DefaultNumUpdaters = 4 // Default number of background updaters
defaultRefreshMailbox = 1000 // Number of on-the-fly background queries
defaultRemoteIdent = "weave:remote" // Ident used for info obtained from mDNS
@@ -126,7 +126,7 @@ func (re *recordEntry) notifyIPObservers() {
func (re *recordEntry) TTL() int {
// if we never set the TTL (eg, when using AddRecord()), return the standard value...
if re.ttl == 0 {
- return int(localTTL)
+ return int(DefaultLocalTTL)
}
return re.ttl
}
@@ -429,6 +429,8 @@ type ZoneConfig struct {
RelevantTime int
// Max pending refresh requests
MaxRefreshRequests int
+ // TTL returnined in local asnwers (with mDNS)
+ LocalTTL int
// Force a specific mDNS client
MDNSClient ZoneMDNSClient
// Force a specific mDNS server
@@ -505,7 +507,12 @@ func NewZoneDb(config ZoneConfig) (zone *ZoneDb, err error) {
}
}
if zone.mdnsSrv == nil {
- if zone.mdnsSrv, err = NewMDNSServer(zone, false); err != nil {
+ mdnsTTL := DefaultLocalTTL
+ if config.LocalTTL > 0 {
+ mdnsTTL = config.LocalTTL
+ }
+
+ if zone.mdnsSrv, err = NewMDNSServer(zone, false, mdnsTTL); err != nil {
return
}
}
diff --git a/site/weavedns.md b/site/weavedns.md
index 23684eab..8d5f7095 100644
--- a/site/weavedns.md
+++ b/site/weavedns.md
@@ -134,7 +134,7 @@ Notice how the ping reaches different addresses.
WeaveDNS removes the addresses of any container that dies. This offers
a simple way to implement redundancy. E.g. if in our example we stop
one of the `pingme` containers and re-run the ping tests, eventually
-(within ~30s at most, since that is the weaveDNS cache expiry time) we
+(within ~30s at most, since that is the weaveDNS [cache expiry time](#ttl)) we
will only be hitting the address of the container that is still alive.
@@ -179,6 +179,25 @@ to the container args:
$ weave launch-dns 10.2.254.1/24 --watch=false
```
+## Using a different TTL value
+
+By default, weaveDNS specifies a TTL of 30 seconds in any reply sent to
+another peer. Peers will honor the TTL received and cache the answer
+until it is considered invalid.
+
+However, you can force a different TTL value by launching weaveDNS with
+the `--ttl` argument:
+
+```bash
+$ weave launch-dns 10.2.254.1/24 --ttl=10
+```
+
+This will shorten the lifespan of answers sent to other peers,
+so you will be effectively reducing the probability of them having stale
+information, but you will also be increasing their resolution times (as
+their cache hit rate will be reduced) and the number of request this
+weaveDNS instance will receive.
+
## Configuring the domain search paths
If you don't supply a domain search path (with `--dns-search=`),
diff --git a/weavedns/main.go b/weavedns/main.go
index 0fc48523..9b6ab270 100644
--- a/weavedns/main.go
+++ b/weavedns/main.go
@@ -24,6 +24,8 @@ func main() {
dnsPort int
httpPort int
wait int
+ ttl int
+ negTTL int
timeout int
udpbuf int
fallback string
@@ -46,9 +48,11 @@ func main() {
flag.IntVar(&dnsPort, "dnsport", weavedns.DefaultServerPort, "port to listen to DNS requests")
flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests")
flag.IntVar(&cacheLen, "cache", weavedns.DefaultCacheLen, "cache length")
+ flag.IntVar(&ttl, "ttl", weavedns.DefaultLocalTTL, "TTL (in secs) for responses for local names")
flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events")
flag.BoolVar(&debug, "debug", false, "output debugging info to stderr")
// advanced options
+ flag.IntVar(&negTTL, "neg-ttl", weavedns.DefaultCacheNegLocalTTL, "negative TTL (in secs) for unanswered queries for local names")
flag.IntVar(&refreshInterval, "refresh", weavedns.DefaultRefreshInterval, "refresh interval (in secs) for local names (0=disable)")
flag.IntVar(&refreshWorkers, "refresh-workers", weavedns.DefaultNumUpdaters, "default number of background updaters")
flag.IntVar(&maxAnswers, "max-answers", weavedns.DefaultMaxAnswers, "maximum number of answers returned to clients (0=unlimited)")
@@ -82,6 +86,7 @@ func main() {
zoneConfig := weavedns.ZoneConfig{
Domain: domain,
Iface: iface,
+ LocalTTL: ttl,
RefreshInterval: refreshInterval,
RefreshWorkers: refreshWorkers,
RelevantTime: relevantTime,
@@ -104,13 +109,15 @@ func main() {
}
srvConfig := weavedns.DNSServerConfig{
- Zone: zone,
- Port: dnsPort,
- CacheLen: cacheLen,
- MaxAnswers: maxAnswers,
- Timeout: timeout,
- UDPBufLen: udpbuf,
- CacheDisabled: cacheDisabled,
+ Zone: zone,
+ Port: dnsPort,
+ CacheLen: cacheLen,
+ LocalTTL: ttl,
+ CacheNegLocalTTL: negTTL,
+ MaxAnswers: maxAnswers,
+ Timeout: timeout,
+ UDPBufLen: udpbuf,
+ CacheDisabled: cacheDisabled,
}
if len(fallback) > 0 {