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

import (
	"sync"
	"time"

Jeromy's avatar
Jeromy committed
7
	peer "gx/ipfs/QmY1xNhBfF9xA1pmD8yejyQAyd77K68qNN6JPM1CN2eiRu/go-libp2p-peer"
Jeromy's avatar
Jeromy committed
8
	ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
9
10
11
12
13
14
15
16
)

// ObservedAddr is an entry for an address reported by our peers.
// We only use addresses that:
// - have been observed more than once. (counter symmetric nats)
// - have been observed recently (10min), because our position in the
//   network, or network port mapppings, may have changed.
type ObservedAddr struct {
17
18
19
	Addr     ma.Multiaddr
	SeenBy   map[string]struct{}
	LastSeen time.Time
20
21
22
23
24
25
26
}

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

27
	addrs map[string]*ObservedAddr
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
	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
		}

		// we only use an address if we've seen it more than once
		// because symmetric nats may cause all our peers to see
		// different port numbers and thus report always different
		// addresses (different ports) for us. These wouldn't be
		// very useful. We make the assumption that if we've
		// connected to two different peers, and they both have
		// reported seeing the same address, it is probably useful.
56
57
58
		//
		// Note: make sure not to double count observers.
		if len(a.SeenBy) > 1 {
59
60
61
62
63
64
			addrs = append(addrs, a.Addr)
		}
	}
	return addrs
}

65
func (oas *ObservedAddrSet) Add(addr ma.Multiaddr, observer ma.Multiaddr) {
66
67
68
69
70
	oas.Lock()
	defer oas.Unlock()

	// for zero-value.
	if oas.addrs == nil {
71
		oas.addrs = make(map[string]*ObservedAddr)
72
73
74
75
		oas.ttl = peer.OwnObservedAddrTTL
	}

	s := addr.String()
76
77
78
79
80
81
82
83
84
	oa, found := oas.addrs[s]

	// first time seeing address.
	if !found {
		oa = &ObservedAddr{
			Addr:   addr,
			SeenBy: make(map[string]struct{}),
		}
		oas.addrs[s] = oa
85
	}
86

87
88
	// mark the observer
	oa.SeenBy[observerGroup(observer)] = struct{}{}
89
	oa.LastSeen = time.Now()
90
91
}

92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 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 {
	return ma.Split(m)[0].String()
}

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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 {
		oas.ttl = peer.OwnObservedAddrTTL
	}
	return oas.ttl
}