Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better symmetric NAT avoidance. #1342

Merged
merged 2 commits into from
Jun 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion p2p/protocol/identify/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) {

// ok! we have the observed version of one of our ListenAddresses!
log.Debugf("added own observed listen addr: %s --> %s", c.LocalMultiaddr(), maddr)
ids.observedAddrs.Add(maddr)
ids.observedAddrs.Add(maddr, c.RemoteMultiaddr())
}

func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool {
Expand Down
47 changes: 36 additions & 11 deletions p2p/protocol/identify/obsaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ import (
// - have been observed recently (10min), because our position in the
// network, or network port mapppings, may have changed.
type ObservedAddr struct {
Addr ma.Multiaddr
LastSeen time.Time
TimesSeen int
Addr ma.Multiaddr
SeenBy map[string]struct{}
LastSeen time.Time
}

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

addrs map[string]ObservedAddr
addrs map[string]*ObservedAddr
ttl time.Duration
}

Expand Down Expand Up @@ -54,29 +54,54 @@ func (oas *ObservedAddrSet) Addrs() []ma.Multiaddr {
// 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.
if a.TimesSeen > 1 {
//
// Note: make sure not to double count observers.
if len(a.SeenBy) > 1 {
addrs = append(addrs, a.Addr)
}
}
return addrs
}

func (oas *ObservedAddrSet) Add(addr ma.Multiaddr) {
func (oas *ObservedAddrSet) Add(addr ma.Multiaddr, observer ma.Multiaddr) {
oas.Lock()
defer oas.Unlock()

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

s := addr.String()
oas.addrs[s] = ObservedAddr{
Addr: addr,
TimesSeen: oas.addrs[s].TimesSeen + 1,
LastSeen: time.Now(),
oa, found := oas.addrs[s]

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

// mark the observer
oa.SeenBy[observerGroup(observer)] = struct{}{}
oa.LastSeen = time.Now()
}

// 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()
}

func (oas *ObservedAddrSet) SetTTL(ttl time.Duration) {
Expand Down
40 changes: 33 additions & 7 deletions p2p/protocol/identify/obsaddr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,56 @@ func TestObsAddrSet(t *testing.T) {
a1 := m("/ip4/1.2.3.4/tcp/1231")
a2 := m("/ip4/1.2.3.4/tcp/1232")
a3 := m("/ip4/1.2.3.4/tcp/1233")
a4 := m("/ip4/1.2.3.4/tcp/1234")
a5 := m("/ip4/1.2.3.4/tcp/1235")
a6 := m("/ip4/1.2.3.6/tcp/1236")
a7 := m("/ip4/1.2.3.7/tcp/1237")

oas := ObservedAddrSet{}

if !addrsMarch(oas.Addrs(), nil) {
t.Error("addrs should be empty")
}

oas.Add(a1)
oas.Add(a2)
oas.Add(a3)
oas.Add(a1, a4)
oas.Add(a2, a4)
oas.Add(a3, a4)

// these are all different so we should not yet get them.
if !addrsMarch(oas.Addrs(), nil) {
t.Error("addrs should _still_ be empty (once)")
}

oas.Add(a1)
// same observer, so should not yet get them.
oas.Add(a1, a4)
oas.Add(a2, a4)
oas.Add(a3, a4)
if !addrsMarch(oas.Addrs(), nil) {
t.Error("addrs should _still_ be empty (same obs)")
}

// different observer, but same observer group.
oas.Add(a1, a5)
oas.Add(a2, a5)
oas.Add(a3, a5)
if !addrsMarch(oas.Addrs(), nil) {
t.Error("addrs should _still_ be empty (same obs group)")
}

oas.Add(a1, a6)
if !addrsMarch(oas.Addrs(), []ma.Multiaddr{a1}) {
t.Error("addrs should only have a1")
}

oas.Add(a2)
oas.Add(a1)
oas.Add(a1)
oas.Add(a2, a5)
oas.Add(a1, a5)
oas.Add(a1, a5)
oas.Add(a2, a6)
oas.Add(a1, a6)
oas.Add(a1, a6)
oas.Add(a2, a7)
oas.Add(a1, a7)
oas.Add(a1, a7)
if !addrsMarch(oas.Addrs(), []ma.Multiaddr{a1, a2}) {
t.Error("addrs should only have a1, a2")
}
Expand Down