diff --git a/nameserver/entry.go b/nameserver/entry.go index 5c0329e871..ad32000c07 100644 --- a/nameserver/entry.go +++ b/nameserver/entry.go @@ -186,13 +186,22 @@ func (es *Entries) first(f func(*Entry) bool) (*Entry, error) { return nil, fmt.Errorf("Not found") } -func (es *Entries) Merge(other router.GossipData) { - es.merge(*other.(*Entries)) +type GossipData struct { + Timestamp int64 + Entries } -func (es *Entries) Encode() [][]byte { +func (g *GossipData) Merge(o router.GossipData) { + other := o.(*GossipData) + g.Entries.merge(other.Entries) + if g.Timestamp < other.Timestamp { + g.Timestamp = other.Timestamp + } +} + +func (g *GossipData) Encode() [][]byte { buf := &bytes.Buffer{} - if err := gob.NewEncoder(buf).Encode(es); err != nil { + if err := gob.NewEncoder(buf).Encode(g); err != nil { panic(err) } return [][]byte{buf.Bytes()} diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index c6822bec79..fffa9d5ff0 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -24,6 +24,9 @@ const ( // Used by prog/weaver/main.go and proxy/create_container_interceptor.go DefaultDomain = "weave.local." + + // Maximum age of acceptable gossip messages (to account for clock skew) + gossipWindow = tombstoneTimeout / 2 ) // Nameserver: gossip-based, in memory nameserver. @@ -78,16 +81,22 @@ func (n *Nameserver) Stop() { n.quit <- struct{}{} } +func (n *Nameserver) broadcastEntries(es ...Entry) error { + if n.gossip != nil { + return n.gossip.GossipBroadcast(&GossipData{ + Entries: Entries(es), + Timestamp: now(), + }) + } + return nil +} + func (n *Nameserver) AddEntry(hostname, containerid string, origin router.PeerName, addr address.Address) error { n.infof("adding entry %s -> %s", hostname, addr.String()) n.Lock() entry := n.entries.add(hostname, containerid, origin, addr) n.Unlock() - - if n.gossip != nil { - return n.gossip.GossipBroadcast(&Entries{entry}) - } - return nil + return n.broadcastEntries(entry) } func (n *Nameserver) Lookup(hostname string) []address.Address { @@ -130,10 +139,7 @@ func (n *Nameserver) ContainerDied(ident string) error { return false }) n.Unlock() - if n.gossip != nil { - return n.gossip.GossipBroadcast(entries) - } - return nil + return n.broadcastEntries(*entries...) } func (n *Nameserver) PeerGone(peer *router.Peer) { @@ -163,10 +169,7 @@ func (n *Nameserver) Delete(hostname, containerid, ipStr string, ip address.Addr return true }) n.Unlock() - if n.gossip != nil { - return n.gossip.GossipBroadcast(entries) - } - return nil + return n.broadcastEntries(*entries...) } func (n *Nameserver) deleteTombstones() { @@ -198,9 +201,12 @@ func (n *Nameserver) String() string { func (n *Nameserver) Gossip() router.GossipData { n.RLock() defer n.RUnlock() - result := make(Entries, len(n.entries)) - copy(result, n.entries) - return &result + gossip := &GossipData{ + Entries: make(Entries, len(n.entries)), + Timestamp: now(), + } + copy(gossip.Entries, n.entries) + return gossip } func (n *Nameserver) OnGossipUnicast(sender router.PeerName, msg []byte) error { @@ -208,12 +214,16 @@ func (n *Nameserver) OnGossipUnicast(sender router.PeerName, msg []byte) error { } func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.GossipData, error) { - var entries Entries - if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(&entries); err != nil { + var gossip GossipData + if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(&gossip); err != nil { return nil, nil, err } - if err := entries.check(); err != nil { + if gossip.Timestamp < now()-int64(gossipWindow/time.Second) { + return nil, nil, fmt.Errorf("Refusing old gossip message: %d", gossip.Timestamp) + } + + if err := gossip.Entries.check(); err != nil { return nil, nil, err } @@ -221,16 +231,16 @@ func (n *Nameserver) receiveGossip(msg []byte) (router.GossipData, router.Gossip defer n.Unlock() if n.peers != nil { - entries.filter(func(e *Entry) bool { + gossip.Entries.filter(func(e *Entry) bool { return n.peers.Fetch(e.Origin) != nil }) } - newEntries := n.entries.merge(entries) + newEntries := n.entries.merge(gossip.Entries) if len(newEntries) > 0 { - return &newEntries, &entries, nil + return &GossipData{Entries: newEntries, Timestamp: now()}, &gossip, nil } - return nil, &entries, nil + return nil, &gossip, nil } // merge received data into state and return "everything new I've