Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filter local addresses (for WAN) and localhost addresses (for LAN) #839

Merged
merged 2 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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