github.com/amazechain/amc@v0.1.3/internal/p2p/netutil/iptrack.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package netutil 18 19 import ( 20 "github.com/amazechain/amc/common/mclock" 21 "time" 22 ) 23 24 // IPTracker predicts the external endpoint, i.e. IP address and port, of the local host 25 // based on statements made by other hosts. 26 type IPTracker struct { 27 window time.Duration 28 contactWindow time.Duration 29 minStatements int 30 clock mclock.Clock 31 statements map[string]ipStatement 32 contact map[string]mclock.AbsTime 33 lastStatementGC mclock.AbsTime 34 lastContactGC mclock.AbsTime 35 } 36 37 type ipStatement struct { 38 endpoint string 39 time mclock.AbsTime 40 } 41 42 // NewIPTracker creates an IP tracker. 43 // 44 // The window parameters configure the amount of past network events which are kept. The 45 // minStatements parameter enforces a minimum number of statements which must be recorded 46 // before any prediction is made. Higher values for these parameters decrease 'flapping' of 47 // predictions as network conditions change. Window duration values should typically be in 48 // the range of minutes. 49 func NewIPTracker(window, contactWindow time.Duration, minStatements int) *IPTracker { 50 return &IPTracker{ 51 window: window, 52 contactWindow: contactWindow, 53 statements: make(map[string]ipStatement), 54 minStatements: minStatements, 55 contact: make(map[string]mclock.AbsTime), 56 clock: mclock.System{}, 57 } 58 } 59 60 // PredictFullConeNAT checks whether the local host is behind full cone NAT. It predicts by 61 // checking whether any statement has been received from a node we didn't contact before 62 // the statement was made. 63 func (it *IPTracker) PredictFullConeNAT() bool { 64 now := it.clock.Now() 65 it.gcContact(now) 66 it.gcStatements(now) 67 for host, st := range it.statements { 68 if c, ok := it.contact[host]; !ok || c > st.time { 69 return true 70 } 71 } 72 return false 73 } 74 75 // PredictEndpoint returns the current prediction of the external endpoint. 76 func (it *IPTracker) PredictEndpoint() string { 77 it.gcStatements(it.clock.Now()) 78 79 // The current strategy is simple: find the endpoint with most statements. 80 counts := make(map[string]int, len(it.statements)) 81 maxcount, max := 0, "" 82 for _, s := range it.statements { 83 c := counts[s.endpoint] + 1 84 counts[s.endpoint] = c 85 if c > maxcount && c >= it.minStatements { 86 maxcount, max = c, s.endpoint 87 } 88 } 89 return max 90 } 91 92 // AddStatement records that a certain host thinks our external endpoint is the one given. 93 func (it *IPTracker) AddStatement(host, endpoint string) { 94 now := it.clock.Now() 95 it.statements[host] = ipStatement{endpoint, now} 96 if time.Duration(now-it.lastStatementGC) >= it.window { 97 it.gcStatements(now) 98 } 99 } 100 101 // AddContact records that a packet containing our endpoint information has been sent to a 102 // certain host. 103 func (it *IPTracker) AddContact(host string) { 104 now := it.clock.Now() 105 it.contact[host] = now 106 if time.Duration(now-it.lastContactGC) >= it.contactWindow { 107 it.gcContact(now) 108 } 109 } 110 111 func (it *IPTracker) gcStatements(now mclock.AbsTime) { 112 it.lastStatementGC = now 113 cutoff := now.Add(-it.window) 114 for host, s := range it.statements { 115 if s.time < cutoff { 116 delete(it.statements, host) 117 } 118 } 119 } 120 121 func (it *IPTracker) gcContact(now mclock.AbsTime) { 122 it.lastContactGC = now 123 cutoff := now.Add(-it.contactWindow) 124 for host, ct := range it.contact { 125 if ct < cutoff { 126 delete(it.contact, host) 127 } 128 } 129 }