Browse Source

p2p: return masked IP (not the actual IP) in addrbook#groupKey

Closes #4846 
Spec https://github.com/tendermint/spec/pull/96
pull/4855/head
Anton Kaliaev 4 years ago
committed by GitHub
parent
commit
3fb80e560d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 22 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +31
    -9
      p2p/netaddress.go
  3. +22
    -13
      p2p/pex/addrbook.go
  4. +68
    -0
      p2p/pex/addrbook_test.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -83,3 +83,4 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- [rpc] \#4805 Attempt to handle panics during panic recovery (@erikgrinaker)
- [types] [\#4764](https://github.com/tendermint/tendermint/pull/4764) Return an error if voting power overflows in `VerifyCommitTrusting` (@melekes)
- [privval] [\#4812](https://github.com/tendermint/tendermint/pull/4812) Retry `GetPubKey/SignVote/SignProposal` a few times before returning an error (@melekes)
- [p2p] [\#4847](https://github.com/tendermint/tendermint/pull/4847) Return masked IP (not the actual IP) in addrbook#groupKey (@melekes)

+ 31
- 9
p2p/netaddress.go View File

@ -312,21 +312,43 @@ var rfc4862 = net.IPNet{IP: net.ParseIP("FE80::"), Mask: net.CIDRMask(64, 128)}
var rfc6052 = net.IPNet{IP: net.ParseIP("64:FF9B::"), Mask: net.CIDRMask(96, 128)}
var rfc6145 = net.IPNet{IP: net.ParseIP("::FFFF:0:0:0"), Mask: net.CIDRMask(96, 128)}
var zero4 = net.IPNet{IP: net.ParseIP("0.0.0.0"), Mask: net.CIDRMask(8, 32)}
var (
// onionCatNet defines the IPv6 address block used to support Tor.
// bitcoind encodes a .onion address as a 16 byte number by decoding the
// address prior to the .onion (i.e. the key hash) base32 into a ten
// byte number. It then stores the first 6 bytes of the address as
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43.
//
// This is the same range used by OnionCat, which is part part of the
// RFC4193 unique local IPv6 range.
//
// In summary the format is:
// { magic 6 bytes, 10 bytes base32 decode of key hash }
onionCatNet = ipNet("fd87:d87e:eb43::", 48, 128)
)
// ipNet returns a net.IPNet struct given the passed IP address string, number
// of one bits to include at the start of the mask, and the total number of bits
// for the mask.
func ipNet(ip string, ones, bits int) net.IPNet {
return net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(ones, bits)}
}
func (na *NetAddress) RFC1918() bool {
return rfc1918_10.Contains(na.IP) ||
rfc1918_192.Contains(na.IP) ||
rfc1918_172.Contains(na.IP)
}
func (na *NetAddress) RFC3849() bool { return rfc3849.Contains(na.IP) }
func (na *NetAddress) RFC3927() bool { return rfc3927.Contains(na.IP) }
func (na *NetAddress) RFC3964() bool { return rfc3964.Contains(na.IP) }
func (na *NetAddress) RFC4193() bool { return rfc4193.Contains(na.IP) }
func (na *NetAddress) RFC4380() bool { return rfc4380.Contains(na.IP) }
func (na *NetAddress) RFC4843() bool { return rfc4843.Contains(na.IP) }
func (na *NetAddress) RFC4862() bool { return rfc4862.Contains(na.IP) }
func (na *NetAddress) RFC6052() bool { return rfc6052.Contains(na.IP) }
func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) }
func (na *NetAddress) RFC3849() bool { return rfc3849.Contains(na.IP) }
func (na *NetAddress) RFC3927() bool { return rfc3927.Contains(na.IP) }
func (na *NetAddress) RFC3964() bool { return rfc3964.Contains(na.IP) }
func (na *NetAddress) RFC4193() bool { return rfc4193.Contains(na.IP) }
func (na *NetAddress) RFC4380() bool { return rfc4380.Contains(na.IP) }
func (na *NetAddress) RFC4843() bool { return rfc4843.Contains(na.IP) }
func (na *NetAddress) RFC4862() bool { return rfc4862.Contains(na.IP) }
func (na *NetAddress) RFC6052() bool { return rfc6052.Contains(na.IP) }
func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) }
func (na *NetAddress) OnionCatTor() bool { return onionCatNet.Contains(na.IP) }
func removeProtocolIfDefined(addr string) string {
if strings.Contains(addr, "://") {


+ 22
- 13
p2p/pex/addrbook.go View File

@ -870,31 +870,36 @@ func (a *addrBook) calcOldBucket(addr *p2p.NetAddress) (int, error) {
}
// Return a string representing the network group of this address.
// This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string
// This is the /16 for IPv4 (e.g. 1.2.0.0), the /32 (/36 for he.net) for IPv6, the string
// "local" for a local address and the string "unroutable" for an unroutable
// address.
func (a *addrBook) groupKey(na *p2p.NetAddress) string {
if a.routabilityStrict && na.Local() {
return groupKeyFor(na, a.routabilityStrict)
}
func groupKeyFor(na *p2p.NetAddress, routabilityStrict bool) string {
if routabilityStrict && na.Local() {
return "local"
}
if a.routabilityStrict && !na.Routable() {
if routabilityStrict && !na.Routable() {
return "unroutable"
}
if ipv4 := na.IP.To4(); ipv4 != nil {
return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(16, 32)}).String()
return na.IP.Mask(net.CIDRMask(16, 32)).String()
}
if na.RFC6145() || na.RFC6052() {
// last four bytes are the ip address
ip := na.IP[12:16]
return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if na.RFC3964() {
ip := na.IP[2:7]
return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
ip := na.IP[2:6]
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if na.RFC4380() {
// teredo tunnels have the last 4 bytes as the v4 address XOR
// 0xff.
@ -902,20 +907,24 @@ func (a *addrBook) groupKey(na *p2p.NetAddress) string {
for i, byte := range na.IP[12:16] {
ip[i] = byte ^ 0xff
}
return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
return ip.Mask(net.CIDRMask(16, 32)).String()
}
if na.OnionCatTor() {
// group is keyed off the first 4 bits of the actual onion key.
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
}
// OK, so now we know ourselves to be a IPv6 address.
// bitcoind uses /32 for everything, except for Hurricane Electric's
// (he.net) IP range, which it uses /36 for.
bits := 32
heNet := &net.IPNet{IP: net.ParseIP("2001:470::"),
Mask: net.CIDRMask(32, 128)}
heNet := &net.IPNet{IP: net.ParseIP("2001:470::"), Mask: net.CIDRMask(32, 128)}
if heNet.Contains(na.IP) {
bits = 36
}
return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String()
ipv6Mask := net.CIDRMask(bits, 128)
return na.IP.Mask(ipv6Mask).String()
}
func (a *addrBook) hash(b []byte) ([]byte, error) {


+ 68
- 0
p2p/pex/addrbook_test.go View File

@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"math"
"net"
"os"
"testing"
"time"
@ -572,6 +573,73 @@ func TestMultipleAddrBookAddressSelection(t *testing.T) {
}
}
func TestAddrBookGroupKey(t *testing.T) {
// non-strict routability
testCases := []struct {
name string
ip string
expKey string
}{
// IPv4 normal.
{"ipv4 normal class a", "12.1.2.3", "12.1.0.0"},
{"ipv4 normal class b", "173.1.2.3", "173.1.0.0"},
{"ipv4 normal class c", "196.1.2.3", "196.1.0.0"},
// IPv6/IPv4 translations.
{"ipv6 rfc3964 with ipv4 encap", "2002:0c01:0203::", "12.1.0.0"},
{"ipv6 rfc4380 toredo ipv4", "2001:0:1234::f3fe:fdfc", "12.1.0.0"},
{"ipv6 rfc6052 well-known prefix with ipv4", "64:ff9b::0c01:0203", "12.1.0.0"},
{"ipv6 rfc6145 translated ipv4", "::ffff:0:0c01:0203", "12.1.0.0"},
// Tor.
{"ipv6 tor onioncat", "fd87:d87e:eb43:1234::5678", "tor:2"},
{"ipv6 tor onioncat 2", "fd87:d87e:eb43:1245::6789", "tor:2"},
{"ipv6 tor onioncat 3", "fd87:d87e:eb43:1345::6789", "tor:3"},
// IPv6 normal.
{"ipv6 normal", "2602:100::1", "2602:100::"},
{"ipv6 normal 2", "2602:0100::1234", "2602:100::"},
{"ipv6 hurricane electric", "2001:470:1f10:a1::2", "2001:470:1000::"},
{"ipv6 hurricane electric 2", "2001:0470:1f10:a1::2", "2001:470:1000::"},
}
for i, tc := range testCases {
nip := net.ParseIP(tc.ip)
key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), false)
assert.Equal(t, tc.expKey, key, "#%d", i)
}
// strict routability
testCases = []struct {
name string
ip string
expKey string
}{
// Local addresses.
{"ipv4 localhost", "127.0.0.1", "local"},
{"ipv6 localhost", "::1", "local"},
{"ipv4 zero", "0.0.0.0", "local"},
{"ipv4 first octet zero", "0.1.2.3", "local"},
// Unroutable addresses.
{"ipv4 invalid bcast", "255.255.255.255", "unroutable"},
{"ipv4 rfc1918 10/8", "10.1.2.3", "unroutable"},
{"ipv4 rfc1918 172.16/12", "172.16.1.2", "unroutable"},
{"ipv4 rfc1918 192.168/16", "192.168.1.2", "unroutable"},
{"ipv6 rfc3849 2001:db8::/32", "2001:db8::1234", "unroutable"},
{"ipv4 rfc3927 169.254/16", "169.254.1.2", "unroutable"},
{"ipv6 rfc4193 fc00::/7", "fc00::1234", "unroutable"},
{"ipv6 rfc4843 2001:10::/28", "2001:10::1234", "unroutable"},
{"ipv6 rfc4862 fe80::/64", "fe80::1234", "unroutable"},
}
for i, tc := range testCases {
nip := net.ParseIP(tc.ip)
key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), true)
assert.Equal(t, tc.expKey, key, "#%d", i)
}
}
func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) {
nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
assert.Equal(t, m, nOld, "old addresses")


Loading…
Cancel
Save