Skip to content

Commit

Permalink
p2p: add ip4/ip6 blocklists (#4795)
Browse files Browse the repository at this point in the history
closes: #4788

default ip4/ip6 blocked ranges https://github.com/spacemeshos/go-spacemesh/blob/a8f86a19fffde6141110b21d398b65bf017d2c57/p2p/host.go#L48-L66C1

to disable them set `"ip4-blocklist": []` or `"ip6-blocklist": []` in config. 
if `private-network` is set to true, blocklists will be ignored. that option meant to be used only in testing networks.
`direct` peers will bypass blocklists validation.
  • Loading branch information
dshulyak committed Aug 22, 2023
1 parent 2582563 commit 3c5eaee
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
77 changes: 75 additions & 2 deletions p2p/gater.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package p2p

import (
"fmt"
"net"

"github.com/libp2p/go-libp2p/core/connmgr"
"github.com/libp2p/go-libp2p/core/control"
"github.com/libp2p/go-libp2p/core/host"
Expand All @@ -11,10 +14,40 @@ import (

var _ connmgr.ConnectionGater = (*gater)(nil)

func newGater(cfg Config) (*gater, error) {
// leaves a small room for outbound connections in order to
// reduce risk of network isolation
g := &gater{
inbound: int(float64(cfg.HighPeers) * cfg.InboundFraction),
outbound: int(float64(cfg.HighPeers) * cfg.OutboundFraction),
direct: map[peer.ID]struct{}{},
}
direct, err := parseIntoAddr(cfg.Direct)
if err != nil {
return nil, err
}
if !cfg.PrivateNetwork {
g.ip4blocklist, err = parseCIDR(cfg.IP4Blocklist)
if err != nil {
return nil, err
}
g.ip6blocklist, err = parseCIDR(cfg.IP6Blocklist)
if err != nil {
return nil, err
}
}
for _, pid := range direct {
g.direct[pid.ID] = struct{}{}
}
return g, nil
}

type gater struct {
h host.Host
inbound, outbound int
direct map[peer.ID]struct{}
ip4blocklist []*net.IPNet
ip6blocklist []*net.IPNet
}

func (g *gater) updateHost(h host.Host) {
Expand All @@ -28,8 +61,11 @@ func (g *gater) InterceptPeerDial(pid peer.ID) bool {
return len(g.h.Network().Peers()) <= g.outbound
}

func (*gater) InterceptAddrDial(pid peer.ID, m multiaddr.Multiaddr) bool {
return true
func (g *gater) InterceptAddrDial(pid peer.ID, m multiaddr.Multiaddr) bool {
if _, exist := g.direct[pid]; exist {
return true
}
return len(g.h.Network().Peers()) <= g.outbound && g.allowed(m)
}

func (g *gater) InterceptAccept(n network.ConnMultiaddrs) bool {
Expand All @@ -43,3 +79,40 @@ func (*gater) InterceptSecured(_ network.Direction, _ peer.ID, _ network.ConnMul
func (*gater) InterceptUpgraded(_ network.Conn) (allow bool, reason control.DisconnectReason) {
return true, 0
}

func (g *gater) allowed(m multiaddr.Multiaddr) bool {
allow := true
multiaddr.ForEach(m, func(c multiaddr.Component) bool {
switch c.Protocol().Code {
case multiaddr.P_IP4:
allow = !inAddrRange(net.IP(c.RawValue()), g.ip4blocklist)
return false
case multiaddr.P_IP6:
allow = !inAddrRange(net.IP(c.RawValue()), g.ip6blocklist)
return false
}
return true
})
return allow
}

func parseCIDR(cidrs []string) ([]*net.IPNet, error) {
ipnets := make([]*net.IPNet, len(cidrs))
for i, cidr := range cidrs {
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, fmt.Errorf("can't parse %s as valid cidr: %w", cidr, err)
}
ipnets[i] = ipnet
}
return ipnets, nil
}

func inAddrRange(ip net.IP, ipnets []*net.IPNet) bool {
for _, ipnet := range ipnets {
if ipnet.Contains(ip) {
return true
}
}
return false
}
34 changes: 34 additions & 0 deletions p2p/gater_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package p2p

import (
"testing"

mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require"
)

func TestGater(t *testing.T) {
cfg := DefaultConfig()
h, err := mocknet.New().GenPeer()
require.NoError(t, err)
gater, err := newGater(cfg)
require.NoError(t, err)
gater.updateHost(h)
for _, tc := range []struct {
address string
allowed bool
}{
{address: "/ip4/127.0.0.1/tcp/8000"},
{address: "/ip4/192.168.0.3/tcp/8000"},
{address: "/ip6/fe80::42:44ff:fe2a:87c5/tcp/8000"},
{address: "/ip6/fe80::42:44ff:fe2a:87c5/tcp/8000"},
{address: "/ip4/95.217.200.84/tcp/8000", allowed: true},
} {
t.Run(tc.address, func(t *testing.T) {
addr, err := multiaddr.NewMultiaddr(tc.address)
require.NoError(t, err)
require.Equal(t, tc.allowed, gater.InterceptAddrDial("", addr))
})
}
}
21 changes: 21 additions & 0 deletions p2p/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ func DefaultConfig() Config {
InboundFraction: 0.8,
OutboundFraction: 1.1,
RelayServer: RelayServer{TTL: 20 * time.Minute, Reservations: 512},
IP4Blocklist: []string{
// localhost
"127.0.0.0/8",
// private networks
"10.0.0.0/8",
"100.64.0.0/10",
"172.16.0.0/12",
"192.168.0.0/16",
// link local
"169.254.0.0/16",
},
IP6Blocklist: []string{
// localhost
"::1/128",
// ULA reserved
"fc00::/7",
// link local
"fe80::/10",
},
}
}

Expand Down Expand Up @@ -84,6 +103,8 @@ type Config struct {
DisableLegacyDiscovery bool `mapstructure:"p2p-disable-legacy-discovery"`
PrivateNetwork bool `mapstructure:"p2p-private-network"`
RelayServer RelayServer `mapstructure:"relay-server"`
IP4Blocklist []string `mapstructure:"ip4-blocklist"`
IP6Blocklist []string `mapstructure:"ip6-blocklist"`
}

type RelayServer struct {
Expand Down

0 comments on commit 3c5eaee

Please sign in to comment.