obsaddr.go 3.03 KB
Newer Older
1
2
3
4
5
6
package identify

import (
	"sync"
	"time"

Jeromy's avatar
Jeromy committed
7
8
	pstore "github.com/libp2p/go-libp2p-peerstore"
	ma "github.com/multiformats/go-multiaddr"
9
10
)

11
12
const ActivationThresh = 4

13
14
// ObservedAddr is an entry for an address reported by our peers.
// We only use addresses that:
15
16
// - have been observed at least 4 times in last 1h. (counter symmetric nats)
// - have been observed at least once recently (1h), because our position in the
17
18
//   network, or network port mapppings, may have changed.
type ObservedAddr struct {
19
20
21
22
23
24
25
26
27
28
	Addr      ma.Multiaddr
	SeenBy    map[string]time.Time
	LastSeen  time.Time
	Activated bool
}

func (oa *ObservedAddr) TryActivate(ttl time.Duration) bool {
	// cleanup SeenBy set
	now := time.Now()
	for k, t := range oa.SeenBy {
29
		if now.Sub(t) > ttl*ActivationThresh {
30
31
32
33
34
35
36
			delete(oa.SeenBy, k)
		}
	}

	// We only activate if in the TTL other peers observed the same address
	// of ours at least 4 times.
	return len(oa.SeenBy) >= ActivationThresh
37
38
39
40
41
42
43
}

// ObservedAddrSet keeps track of a set of ObservedAddrs
// the zero-value is ready to be used.
type ObservedAddrSet struct {
	sync.Mutex // guards whole datastruct.

44
	addrs map[string]*ObservedAddr
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	ttl   time.Duration
}

func (oas *ObservedAddrSet) Addrs() []ma.Multiaddr {
	oas.Lock()
	defer oas.Unlock()

	// for zero-value.
	if oas.addrs == nil {
		return nil
	}

	now := time.Now()
	addrs := make([]ma.Multiaddr, 0, len(oas.addrs))
	for s, a := range oas.addrs {
		// remove timed out addresses.
		if now.Sub(a.LastSeen) > oas.ttl {
			delete(oas.addrs, s)
			continue
		}

66
		if a.Activated || a.TryActivate(oas.ttl) {
67
68
69
70
71
72
			addrs = append(addrs, a.Addr)
		}
	}
	return addrs
}

73
func (oas *ObservedAddrSet) Add(addr ma.Multiaddr, observer ma.Multiaddr) {
74
75
76
77
78
	oas.Lock()
	defer oas.Unlock()

	// for zero-value.
	if oas.addrs == nil {
79
		oas.addrs = make(map[string]*ObservedAddr)
Jeromy's avatar
Jeromy committed
80
		oas.ttl = pstore.OwnObservedAddrTTL
81
82
83
	}

	s := addr.String()
84
85
86
87
88
89
	oa, found := oas.addrs[s]

	// first time seeing address.
	if !found {
		oa = &ObservedAddr{
			Addr:   addr,
90
			SeenBy: make(map[string]time.Time),
91
92
		}
		oas.addrs[s] = oa
93
	}
94

95
	// mark the observer
96
	oa.SeenBy[observerGroup(observer)] = time.Now()
97
	oa.LastSeen = time.Now()
98
99
}

100
101
102
103
104
105
106
107
108
109
110
// observerGroup is a function that determines what part of
// a multiaddr counts as a different observer. for example,
// two ipfs nodes at the same IP/TCP transport would get
// the exact same NAT mapping; they would count as the
// same observer. This may protect against NATs who assign
// different ports to addresses at different IP hosts, but
// not TCP ports.
//
// Here, we use the root multiaddr address. This is mostly
// IP addresses. In practice, this is what we want.
func observerGroup(m ma.Multiaddr) string {
111
	//TODO: If IPv6 rolls out we should mark /64 routing zones as one group
112
113
114
	return ma.Split(m)[0].String()
}

115
116
117
118
119
120
121
122
123
124
125
func (oas *ObservedAddrSet) SetTTL(ttl time.Duration) {
	oas.Lock()
	defer oas.Unlock()
	oas.ttl = ttl
}

func (oas *ObservedAddrSet) TTL() time.Duration {
	oas.Lock()
	defer oas.Unlock()
	// for zero-value.
	if oas.addrs == nil {
Jeromy's avatar
Jeromy committed
126
		oas.ttl = pstore.OwnObservedAddrTTL
127
128
129
	}
	return oas.ttl
}