diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 4ee07c958..426cabafd 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -239,6 +239,10 @@ "ImportPath": "github.com/whyrusleeping/iptb", "Rev": "3970c95a864f1a40037f796ff596607ce8ae43be" }, + { + "ImportPath": "github.com/whyrusleeping/multiaddr-filter", + "Rev": "15837fcc356fddef27c634b0f6379b3b7f259114" + }, { "ImportPath": "golang.org/x/crypto/blowfish", "Rev": "c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3" diff --git a/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask.go b/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask.go new file mode 100644 index 000000000..91208fe04 --- /dev/null +++ b/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask.go @@ -0,0 +1,19 @@ +package mask + +import ( + "errors" + "net" + "strings" +) + +func NewMask(a string) (*net.IPNet, error) { + parts := strings.Split(a, "/") + if len(parts) == 5 && parts[1] == "ip4" && parts[3] == "ipcidr" { + _, ipn, err := net.ParseCIDR(parts[2] + "/" + parts[4]) + if err != nil { + return nil, err + } + return ipn, nil + } + return nil, errors.New("invalid format") +} diff --git a/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask_test.go b/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask_test.go new file mode 100644 index 000000000..4507dca68 --- /dev/null +++ b/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter/mask_test.go @@ -0,0 +1,36 @@ +package mask + +import ( + "net" + "testing" +) + +func TestFiltered(t *testing.T) { + var tests = map[string]map[string]bool{ + "/ip4/10.0.0.0/ipcidr/8": map[string]bool{ + "10.3.3.4": true, + "10.3.4.4": true, + "10.4.4.4": true, + "15.52.34.3": false, + }, + "/ip4/192.168.0.0/ipcidr/16": map[string]bool{ + "192.168.0.0": true, + "192.168.1.0": true, + "192.1.0.0": false, + "10.4.4.4": false, + }, + } + + for mask, set := range tests { + m, err := NewMask(mask) + if err != nil { + t.Fatal(err) + } + for addr, val := range set { + ip := net.ParseIP(addr) + if m.Contains(ip) != val { + t.Fatalf("expected contains(%s, %s) == %s", mask, addr, val) + } + } + } +} diff --git a/core/core.go b/core/core.go index 877ffe9c0..e3111a172 100644 --- a/core/core.go +++ b/core/core.go @@ -13,17 +13,17 @@ import ( "errors" "fmt" "io" + "net" "time" b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58" ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup" ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" - metrics "github.com/ipfs/go-ipfs/metrics" - eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" - diag "github.com/ipfs/go-ipfs/diagnostics" + metrics "github.com/ipfs/go-ipfs/metrics" ic "github.com/ipfs/go-ipfs/p2p/crypto" discovery "github.com/ipfs/go-ipfs/p2p/discovery" p2phost "github.com/ipfs/go-ipfs/p2p/host" @@ -32,6 +32,7 @@ import ( swarm "github.com/ipfs/go-ipfs/p2p/net/swarm" addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr" peer "github.com/ipfs/go-ipfs/p2p/peer" + eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" routing "github.com/ipfs/go-ipfs/routing" dht "github.com/ipfs/go-ipfs/routing/dht" @@ -254,7 +255,18 @@ func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption Routin // Set reporter n.Reporter = metrics.NewBandwidthCounter() - peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter) + // get undialable addrs from config + cfg := n.Repo.Config() + var addrfilter []*net.IPNet + for _, s := range cfg.DialBlocklist { + f, err := mamask.NewMask(s) + if err != nil { + return fmt.Errorf("incorrectly formatter address filter in config: %s", s) + } + addrfilter = append(addrfilter, f) + } + + peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter) if err != nil { return err } @@ -508,12 +520,12 @@ func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) { return listen, nil } -type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) +type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error) var DefaultHostOption HostOption = constructPeerHost // isolates the complex initialization steps -func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) { +func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error) { // no addresses to begin with. we'll start later. network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr) diff --git a/p2p/net/swarm/swarm.go b/p2p/net/swarm/swarm.go index 15a5bbddb..489d6fec3 100644 --- a/p2p/net/swarm/swarm.go +++ b/p2p/net/swarm/swarm.go @@ -4,6 +4,7 @@ package swarm import ( "fmt" + "net" "sync" "time" @@ -50,6 +51,9 @@ type Swarm struct { notifmu sync.RWMutex notifs map[inet.Notifiee]ps.Notifiee + // filters for addresses that shouldnt be dialed + filters []*net.IPNet + cg ctxgroup.ContextGroup bwc metrics.Reporter } @@ -84,6 +88,10 @@ func (s *Swarm) teardown() error { return s.swarm.Close() } +func (s *Swarm) AddDialFilter(f *net.IPNet) { + s.filters = append(s.filters, f) +} + // CtxGroup returns the Context Group of the swarm func filterAddrs(listenAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) { if len(listenAddrs) > 0 { diff --git a/p2p/net/swarm/swarm_dial.go b/p2p/net/swarm/swarm_dial.go index aacee6ec4..534f916d1 100644 --- a/p2p/net/swarm/swarm_dial.go +++ b/p2p/net/swarm/swarm_dial.go @@ -303,6 +303,8 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { ila, _ := s.InterfaceListenAddresses() remoteAddrs = addrutil.Subtract(remoteAddrs, ila) remoteAddrs = addrutil.Subtract(remoteAddrs, s.peers.Addrs(s.local)) + remoteAddrs = s.filterAddrs(remoteAddrs) + log.Debugf("%s swarm dialing %s -- local:%s remote:%s", s.local, p, s.ListenAddresses(), remoteAddrs) if len(remoteAddrs) == 0 { err := errors.New("peer has no addresses") @@ -454,6 +456,32 @@ func (s *Swarm) dialAddr(ctx context.Context, d *conn.Dialer, p peer.ID, addr ma return connC, nil } +func (s *Swarm) filterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { + var out []ma.Multiaddr + for _, a := range addrs { + if !s.addrBlocked(a) { + out = append(out, a) + } + } + return out +} + +func (s *Swarm) addrBlocked(a ma.Multiaddr) bool { + _, addr, err := manet.DialArgs(a) + if err != nil { + // if we cant parse it, its probably not blocked + return false + } + + ip := net.ParseIP(addr) + for _, f := range s.filters { + if f.Contains(ip) { + return true + } + } + return false +} + // dialConnSetup is the setup logic for a connection from the dial side. it // needs to add the Conn to the StreamSwarm, then run newConnSetup func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error) { diff --git a/repo/config/config.go b/repo/config/config.go index c9a8aabec..e7474cf7e 100644 --- a/repo/config/config.go +++ b/repo/config/config.go @@ -26,6 +26,7 @@ type Config struct { Tour Tour // local node's tour position Gateway Gateway // local node's gateway server options SupernodeRouting SupernodeClientConfig // local node's routing servers (if SupernodeRouting enabled) + DialBlocklist []string Log Log } diff --git a/util/sadhack/godep.go b/util/sadhack/godep.go index c38bbb1e4..47a2350bb 100644 --- a/util/sadhack/godep.go +++ b/util/sadhack/godep.go @@ -6,4 +6,5 @@ import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-hum // similar to the above, only used in the tests makefile import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/iptb" + import _ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/chriscool/go-sleep"