Skip to content

Commit

Permalink
p2p/netutil: fix PredictFullConeNAT
Browse files Browse the repository at this point in the history
The discovery protocol sends pings to previously-unseen nodes after
receiving a ping from them. This means there will always be a contact
entry and PredictFullConeNAT will always return false. Fix it by
checking that the contact occurred after the IP statement.
  • Loading branch information
fjl committed Oct 11, 2018
1 parent 6c81ee8 commit c9813cd
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 6 deletions.
12 changes: 7 additions & 5 deletions p2p/netutil/iptrack.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type IPTracker struct {
clock mclock.Clock
statements map[string]ipStatement
contact map[string]mclock.AbsTime
// TODO add DistinctNetSet for additional protection
}

type ipStatement struct {
Expand All @@ -57,13 +56,15 @@ func NewIPTracker(window, contactWindow time.Duration, minStatements int) *IPTra
}
}

// PredictFullConeNAT checks whether the local host is behind full cone NAT.
// PredictFullConeNAT checks whether the local host is behind full cone NAT. It predicts by
// checking whether any statement has been received from a node we didn't contact before
// the statement was made.
func (it *IPTracker) PredictFullConeNAT() bool {
now := it.clock.Now()
it.gcContact(now)
it.gcStatements(now)
for host := range it.statements {
if _, ok := it.contact[host]; !ok {
for host, st := range it.statements {
if c, ok := it.contact[host]; !ok || c > st.time {
return true
}
}
Expand Down Expand Up @@ -92,7 +93,8 @@ func (it *IPTracker) AddStatement(host, endpoint string) {
it.statements[host] = ipStatement{endpoint, it.clock.Now()}
}

// AddContact records that a packet containing endpoint information has been sent to a certain host.
// AddContact records that a packet containing our endpoint information has been sent to a
// certain host.
func (it *IPTracker) AddContact(host string) {
it.contact[host] = it.clock.Now()
}
Expand Down
26 changes: 25 additions & 1 deletion p2p/netutil/iptrack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package netutil

import (
"fmt"
"testing"
"time"

Expand All @@ -27,6 +28,7 @@ const (
opStatement = iota
opContact
opPredict
opCheckFullCone
)

type iptrackTestEvent struct {
Expand Down Expand Up @@ -55,8 +57,25 @@ func TestIPTracker(t *testing.T) {
{opStatement, 10100, "127.0.0.1", "127.0.0.2"},
{opPredict, 10200, "127.0.0.1", ""},
},
"fullcone": {
{opContact, 0, "", "127.0.0.2"},
{opStatement, 10, "127.0.0.1", "127.0.0.2"},
{opContact, 2000, "", "127.0.0.3"},
{opStatement, 2010, "127.0.0.1", "127.0.0.3"},
{opContact, 3000, "", "127.0.0.4"},
{opStatement, 3010, "127.0.0.1", "127.0.0.4"},
{opCheckFullCone, 3500, "false", ""},
},
"fullcone_2": {
{opContact, 0, "", "127.0.0.2"},
{opStatement, 10, "127.0.0.1", "127.0.0.2"},
{opContact, 2000, "", "127.0.0.3"},
{opStatement, 2010, "127.0.0.1", "127.0.0.3"},
{opStatement, 3000, "127.0.0.1", "127.0.0.4"},
{opContact, 3010, "", "127.0.0.4"},
{opCheckFullCone, 3500, "true", ""},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) { runIPTrackerTest(t, test) })
}
Expand All @@ -80,6 +99,11 @@ func runIPTrackerTest(t *testing.T, evs []iptrackTestEvent) {
if pred := it.PredictEndpoint(); pred != ev.ip {
t.Errorf("op %d: wrong prediction %q, want %q", i, pred, ev.ip)
}
case opCheckFullCone:
pred := fmt.Sprintf("%t", it.PredictFullConeNAT())
if pred != ev.ip {
t.Errorf("op %d: wrong prediction %s, want %s", i, pred, ev.ip)
}
}
}
}

0 comments on commit c9813cd

Please sign in to comment.