From e94c190c5620adb2d3ab4f6b724010c0890ae255 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 17 May 2023 11:49:17 +0300 Subject: [PATCH 1/2] filter local addresses (for WAN) and localhost addresses (for LAN) --- dht.go | 8 ++++++++ dht_options.go | 16 +++++++++++++--- dual/dual.go | 9 ++++++++- internal/config/config.go | 2 ++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/dht.go b/dht.go index 4ebfd8ac0..c357f6169 100644 --- a/dht.go +++ b/dht.go @@ -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 @@ -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), @@ -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) } diff --git a/dht_options.go b/dht_options.go index 208786a61..edf46f161 100644 --- a/dht_options.go +++ b/dht_options.go @@ -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 @@ -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 + } +} diff --git a/dual/dual.go b/dual/dual.go index e38967513..d75555b30 100644 --- a/dual/dual.go +++ b/dual/dual.go @@ -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 @@ -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" ) @@ -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 { @@ -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 { diff --git a/internal/config/config.go b/internal/config/config.go index 3d5c0734b..530de1014 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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. @@ -58,6 +59,7 @@ type Config struct { } BootstrapPeers func() []peer.AddrInfo + AddressFilter func([]ma.Multiaddr) []ma.Multiaddr // test specific Config options DisableFixLowPeers bool From 4c5500412993b27e608842b52575e7000807bb47 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Tue, 23 May 2023 09:32:22 +0200 Subject: [PATCH 2/2] added tests for addrfilter --- dht_test.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/dht_test.go b/dht_test.go index 56105bd1a..9ed33eb64 100644 --- a/dht_test.go +++ b/dht_test.go @@ -14,6 +14,7 @@ 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" @@ -21,6 +22,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/multiformats/go-multihash" "github.com/multiformats/go-multistream" @@ -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))) +}