Skip to content

Commit

Permalink
Merge pull request #839 from libp2p/addr-filter
Browse files Browse the repository at this point in the history
filter local addresses (for WAN) and localhost addresses (for LAN)
  • Loading branch information
guillaumemichel authored Jun 5, 2023
2 parents f4e8e86 + 4c55004 commit 8d07d57
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 4 deletions.
8 changes: 8 additions & 0 deletions dht.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ type IpfsDHT struct {

// configuration variables for tests
testAddressUpdateProcessing bool

// addrFilter is used to filter the addresses we put into the peer store.
// Mostly used to filter out localhost and local addresses.
addrFilter func([]ma.Multiaddr) []ma.Multiaddr
}

// Assert that IPFS assumptions about interfaces aren't broken. These aren't a
Expand Down Expand Up @@ -301,6 +305,7 @@ func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, err
queryPeerFilter: cfg.QueryPeerFilter,
routingTablePeerFilter: cfg.RoutingTable.PeerFilter,
rtPeerDiversityFilter: cfg.RoutingTable.DiversityFilter,
addrFilter: cfg.AddressFilter,

fixLowPeersChan: make(chan struct{}, 1),

Expand Down Expand Up @@ -884,5 +889,8 @@ func (dht *IpfsDHT) maybeAddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Dura
if p == dht.self || dht.host.Network().Connectedness(p) == network.Connected {
return
}
if dht.addrFilter != nil {
addrs = dht.addrFilter(addrs)
}
dht.peerstore.AddAddrs(p, addrs, ttl)
}
16 changes: 13 additions & 3 deletions dht_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import (

dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config"
"github.com/libp2p/go-libp2p-kad-dht/providers"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"

"github.com/libp2p/go-libp2p-kbucket/peerdiversity"
record "github.com/libp2p/go-libp2p-record"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"

ds "github.com/ipfs/go-datastore"
ma "github.com/multiformats/go-multiaddr"
)

// ModeOpt describes what mode the dht should operate in
Expand Down Expand Up @@ -337,3 +337,13 @@ func OptimisticProvideJobsPoolSize(size int) Option {
return nil
}
}

// AddressFilter allows to configure the address filtering function.
// This function is run before addresses are added to the peerstore.
// It is most useful to avoid adding localhost / local addresses.
func AddressFilter(f func([]ma.Multiaddr) []ma.Multiaddr) Option {
return func(c *dhtcfg.Config) error {
c.AddressFilter = f
return nil
}
}
91 changes: 91 additions & 0 deletions dht_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import (
"testing"
"time"

"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/routing"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/multiformats/go-multihash"
"github.com/multiformats/go-multistream"

Expand Down Expand Up @@ -2131,3 +2133,92 @@ func TestPreconnectedNodes(t *testing.T) {
require.Equal(t, len(peers), 1, "why is there more than one peer?")
require.Equal(t, h1.ID(), peers[0], "could not find peer")
}

func TestAddrFilter(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// generate a bunch of addresses
publicAddrs := []ma.Multiaddr{
ma.StringCast("/ip4/1.2.3.1/tcp/123"),
ma.StringCast("/ip4/160.160.160.160/tcp/1600"),
ma.StringCast("/ip6/2001::10/tcp/123"),
}
privAddrs := []ma.Multiaddr{
ma.StringCast("/ip4/192.168.1.100/tcp/123"),
ma.StringCast("/ip4/172.16.10.10/tcp/123"),
ma.StringCast("/ip4/10.10.10.10/tcp/123"),
ma.StringCast("/ip6/fc00::10/tcp/123"),
}
loopbackAddrs := []ma.Multiaddr{
ma.StringCast("/ip4/127.0.0.100/tcp/123"),
ma.StringCast("/ip6/::1/tcp/123"),
}

allAddrs := append(publicAddrs, privAddrs...)
allAddrs = append(allAddrs, loopbackAddrs...)

// generate different address filters
acceptAllFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
return addrs
})
rejectAllFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
return []ma.Multiaddr{}
})
publicIpFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
return ma.FilterAddrs(addrs, manet.IsPublicAddr)
})
localIpFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { return !manet.IsIPLoopback(a) })
})

// generate peerid for "remote" peer
_, pub, err := crypto.GenerateKeyPair(
crypto.Ed25519, // Select your key type. Ed25519 are nice short
-1, // Select key length when possible (i.e. RSA).
)
require.NoError(t, err)
peerid, err := peer.IDFromPublicKey(pub)
require.NoError(t, err)

// DHT accepting all addresses
d0 := setupDHT(ctx, t, false, acceptAllFilter)

// peerstore should only contain self
require.Equal(t, 1, d0.host.Peerstore().Peers().Len())

d0.maybeAddAddrs(peerid, allAddrs, time.Minute)
require.Equal(t, 2, d0.host.Peerstore().Peers().Len())
for _, a := range allAddrs {
// check that the peerstore contains all addresses of the remote peer
require.Contains(t, d0.host.Peerstore().Addrs(peerid), a)
}

// DHT rejecting all addresses
d1 := setupDHT(ctx, t, false, rejectAllFilter)
d1.maybeAddAddrs(peerid, allAddrs, time.Minute)
// remote peer should not be added to peerstore (all addresses rejected)
require.Equal(t, 1, d1.host.Peerstore().Peers().Len())

// DHT accepting only public addresses
d2 := setupDHT(ctx, t, false, publicIpFilter)
d2.maybeAddAddrs(peerid, allAddrs, time.Minute)
for _, a := range publicAddrs {
// check that the peerstore contains only public addresses of the remote peer
require.Contains(t, d2.host.Peerstore().Addrs(peerid), a)
}
require.Equal(t, len(publicAddrs), len(d2.host.Peerstore().Addrs(peerid)))

// DHT accepting only non-loopback addresses
d3 := setupDHT(ctx, t, false, localIpFilter)
d3.maybeAddAddrs(peerid, allAddrs, time.Minute)
for _, a := range publicAddrs {
// check that the peerstore contains only non-loopback addresses of the remote peer
require.Contains(t, d3.host.Peerstore().Addrs(peerid), a)
}
for _, a := range privAddrs {
// check that the peerstore contains only non-loopback addresses of the remote peer
require.Contains(t, d3.host.Peerstore().Addrs(peerid), a)
}
require.Equal(t, len(publicAddrs)+len(privAddrs), len(d3.host.Peerstore().Addrs(peerid)))
}
9 changes: 8 additions & 1 deletion dual/dual.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package dual provides an implementaiton of a split or "dual" dht, where two parallel instances
// Package dual provides an implementation of a split or "dual" dht, where two parallel instances
// are maintained for the global internet and the local LAN respectively.
package dual

Expand All @@ -19,6 +19,7 @@ import (
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/routing"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"

"github.com/hashicorp/go-multierror"
)
Expand Down Expand Up @@ -101,6 +102,8 @@ func New(ctx context.Context, h host.Host, options ...Option) (*DHT, error) {
dht.QueryFilter(dht.PublicQueryFilter),
dht.RoutingTableFilter(dht.PublicRoutingTableFilter),
dht.RoutingTablePeerDiversityFilter(dht.NewRTPeerDiversityFilter(h, maxPrefixCountPerCpl, maxPrefixCount)),
// filter out all private addresses
dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { return ma.FilterAddrs(addrs, manet.IsPublicAddr) }),
),
)
if err != nil {
Expand All @@ -111,6 +114,10 @@ func New(ctx context.Context, h host.Host, options ...Option) (*DHT, error) {
dht.ProtocolExtension(LanExtension),
dht.QueryFilter(dht.PrivateQueryFilter),
dht.RoutingTableFilter(dht.PrivateRoutingTableFilter),
// filter out localhost IP addresses
dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { return !manet.IsIPLoopback(a) })
}),
),
)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
ma "github.com/multiformats/go-multiaddr"
)

// DefaultPrefix is the application specific prefix attached to all DHT protocols by default.
Expand Down Expand Up @@ -58,6 +59,7 @@ type Config struct {
}

BootstrapPeers func() []peer.AddrInfo
AddressFilter func([]ma.Multiaddr) []ma.Multiaddr

// test specific Config options
DisableFixLowPeers bool
Expand Down

0 comments on commit 8d07d57

Please sign in to comment.