Skip to content

Commit

Permalink
feat: add a transport config section
Browse files Browse the repository at this point in the history
This way, users can disable transports (especially QUIC), and set muxer/security
transport priorities.
  • Loading branch information
Stebalien committed Jun 16, 2020
1 parent eb1300c commit 8c6c312
Show file tree
Hide file tree
Showing 15 changed files with 595 additions and 141 deletions.
8 changes: 5 additions & 3 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Headers.
cmds.BoolOption(migrateKwd, "If true, assume yes at the migrate prompt. If false, assume no."),
cmds.BoolOption(enablePubSubKwd, "Instantiate the ipfs daemon with the experimental pubsub feature enabled."),
cmds.BoolOption(enableIPNSPubSubKwd, "Enable IPNS record distribution through pubsub; enables pubsub."),
cmds.BoolOption(enableMultiplexKwd, "Add the experimental 'go-multiplex' stream muxer to libp2p on construction.").WithDefault(true),
cmds.BoolOption(enableMultiplexKwd, "DEPRECATED"),

// TODO: add way to override addresses. tricky part: updating the config if also --init.
// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
Expand Down Expand Up @@ -296,7 +296,10 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
offline, _ := req.Options[offlineKwd].(bool)
ipnsps, _ := req.Options[enableIPNSPubSubKwd].(bool)
pubsub, _ := req.Options[enablePubSubKwd].(bool)
mplex, _ := req.Options[enableMultiplexKwd].(bool)
if _, hasMplex := req.Options[enableMultiplexKwd]; hasMplex {
log.Errorf("The mplex multiplexer has been enabled by default and the experimental %s flag has been removed.")
log.Errorf("To disable this multiplexer, please configure `Swarm.Transports.Multiplexers'.")
}

// Start assembling node config
ncfg := &core.BuildCfg{
Expand All @@ -307,7 +310,6 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
ExtraOpts: map[string]bool{
"pubsub": pubsub,
"ipnsps": ipnsps,
"mplex": mplex,
},
//TODO(Kubuxu): refactor Online vs Offline by adding Permanent vs Ephemeral
}
Expand Down
24 changes: 20 additions & 4 deletions core/node/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
blockstore "github.com/ipfs/go-ipfs-blockstore"
config "github.com/ipfs/go-ipfs-config"
util "github.com/ipfs/go-ipfs-util"
log "github.com/ipfs/go-log"
peer "github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"

Expand All @@ -22,6 +23,8 @@ import (
"go.uber.org/fx"
)

var logger = log.Logger("core:constructor")

var BaseLibP2P = fx.Options(
fx.Provide(libp2p.UserAgent),
fx.Provide(libp2p.PNet),
Expand Down Expand Up @@ -108,19 +111,32 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option {
autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle))
}

// Gather all the options
// If `cfg.Swarm.DisableRelay` is set and `Network.Relay` isn't, use the former.
enableRelay := cfg.Swarm.Transports.Network.Relay.WithDefault(!cfg.Swarm.DisableRelay) //nolint

// Warn about a deprecated option.
//nolint
if cfg.Swarm.DisableRelay {
logger.Error("The `Swarm.DisableRelay' config field is deprecated.")
if enableRelay {
logger.Error("`Swarm.DisableRelay' has been overridden by `Swarm.Transports.Network.Relay'")
} else {
logger.Error("Use the `Swarm.Transports.Network.Relay' config field instead")
}
}

// Gather all the options
opts := fx.Options(
BaseLibP2P,

fx.Provide(libp2p.AddrFilters(cfg.Swarm.AddrFilters)),
fx.Provide(libp2p.AddrsFactory(cfg.Addresses.Announce, cfg.Addresses.NoAnnounce)),
fx.Provide(libp2p.SmuxTransport(bcfg.getOpt("mplex"))),
fx.Provide(libp2p.Relay(cfg.Swarm.DisableRelay, cfg.Swarm.EnableRelayHop)),
fx.Provide(libp2p.SmuxTransport(cfg.Swarm.Transports)),
fx.Provide(libp2p.Relay(enableRelay, cfg.Swarm.EnableRelayHop)),
fx.Invoke(libp2p.StartListening(cfg.Addresses.Swarm)),
fx.Invoke(libp2p.SetupDiscovery(cfg.Discovery.MDNS.Enabled, cfg.Discovery.MDNS.Interval)),

fx.Provide(libp2p.Security(!bcfg.DisableEncryptedConnections, cfg.Experimental.OverrideSecurityTransports)),
fx.Provide(libp2p.Security(!bcfg.DisableEncryptedConnections, cfg.Swarm.Transports)),

fx.Provide(libp2p.Routing),
fx.Provide(libp2p.BaseRouting),
Expand Down
31 changes: 31 additions & 0 deletions core/node/libp2p/libp2p.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package libp2p

import (
"sort"
"time"

version "github.com/ipfs/go-ipfs"
config "github.com/ipfs/go-ipfs-config"

logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p"
Expand Down Expand Up @@ -48,3 +50,32 @@ func simpleOpt(opt libp2p.Option) func() (opts Libp2pOpts, err error) {
return
}
}

type priorityOption struct {
priority, defaultPriority config.Priority
opt libp2p.Option
}

func prioritizeOptions(opts []priorityOption) libp2p.Option {
type popt struct {
priority int64
opt libp2p.Option
}
enabledOptions := make([]popt, 0, len(opts))
for _, o := range opts {
if prio, ok := o.priority.WithDefault(o.defaultPriority); ok {
enabledOptions = append(enabledOptions, popt{
priority: prio,
opt: o.opt,
})
}
}
sort.Slice(enabledOptions, func(i, j int) bool {
return enabledOptions[i].priority > enabledOptions[j].priority
})
p2pOpts := make([]libp2p.Option, len(enabledOptions))
for i, opt := range enabledOptions {
p2pOpts[i] = opt.opt
}
return libp2p.ChainOptions(p2pOpts...)
}
9 changes: 4 additions & 5 deletions core/node/libp2p/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ import (
relay "github.com/libp2p/go-libp2p-circuit"
)

func Relay(disable, enableHop bool) func() (opts Libp2pOpts, err error) {
func Relay(enableRelay, enableHop bool) func() (opts Libp2pOpts, err error) {
return func() (opts Libp2pOpts, err error) {
if disable {
// Enabled by default.
opts.Opts = append(opts.Opts, libp2p.DisableRelay())
} else {
if enableRelay {
relayOpts := []relay.RelayOpt{}
if enableHop {
relayOpts = append(relayOpts, relay.OptHop)
}
opts.Opts = append(opts.Opts, libp2p.EnableRelay(relayOpts...))
} else {
opts.Opts = append(opts.Opts, libp2p.DisableRelay())
}
return
}
Expand Down
39 changes: 39 additions & 0 deletions core/node/libp2p/sec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package libp2p

import (
config "github.com/ipfs/go-ipfs-config"
"github.com/libp2p/go-libp2p"
noise "github.com/libp2p/go-libp2p-noise"
secio "github.com/libp2p/go-libp2p-secio"
tls "github.com/libp2p/go-libp2p-tls"
)

func Security(enabled bool, tptConfig config.Transports) interface{} {
if !enabled {
return func() (opts Libp2pOpts) {
// TODO: shouldn't this be Errorf to guarantee visibility?
log.Warnf(`Your IPFS node has been configured to run WITHOUT ENCRYPTED CONNECTIONS.
You will not be able to connect to any nodes configured to use encrypted connections`)
opts.Opts = append(opts.Opts, libp2p.NoSecurity)
return opts
}
}

// Using the new config options.
return func() (opts Libp2pOpts) {
opts.Opts = append(opts.Opts, prioritizeOptions([]priorityOption{{
priority: tptConfig.Security.TLS,
defaultPriority: 100,
opt: libp2p.Security(tls.ID, tls.New),
}, {
priority: tptConfig.Security.SECIO,
defaultPriority: 200,
opt: libp2p.Security(secio.ID, secio.New),
}, {
priority: tptConfig.Security.Noise,
defaultPriority: 300,
opt: libp2p.Security(noise.ID, noise.New),
}}))
return opts
}
}
76 changes: 49 additions & 27 deletions core/node/libp2p/smux.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,76 @@
package libp2p

import (
"fmt"
"os"
"strings"

config "github.com/ipfs/go-ipfs-config"
"github.com/libp2p/go-libp2p"
smux "github.com/libp2p/go-libp2p-core/mux"
mplex "github.com/libp2p/go-libp2p-mplex"
yamux "github.com/libp2p/go-libp2p-yamux"
)

func makeSmuxTransportOption(mplexExp bool) libp2p.Option {
func yamuxTransport() smux.Multiplexer {
tpt := *yamux.DefaultTransport
tpt.AcceptBacklog = 512
if os.Getenv("YAMUX_DEBUG") != "" {
tpt.LogOutput = os.Stderr
}

return &tpt
}

func makeSmuxTransportOption(tptConfig config.Transports) (libp2p.Option, error) {
const yamuxID = "/yamux/1.0.0"
const mplexID = "/mplex/6.7.0"

ymxtpt := *yamux.DefaultTransport
ymxtpt.AcceptBacklog = 512

if os.Getenv("YAMUX_DEBUG") != "" {
ymxtpt.LogOutput = os.Stderr
}

muxers := map[string]smux.Multiplexer{yamuxID: &ymxtpt}
if mplexExp {
muxers[mplexID] = mplex.DefaultTransport
}

// Allow muxer preference order overriding
order := []string{yamuxID, mplexID}
if prefs := os.Getenv("LIBP2P_MUX_PREFS"); prefs != "" {
order = strings.Fields(prefs)
}
// Using legacy LIBP2P_MUX_PREFS variable.
log.Error("LIBP2P_MUX_PREFS is now deprecated.")
log.Error("Use the `Swarm.Transports.Multiplexers' config field.")
muxers := strings.Fields(prefs)
enabled := make(map[string]bool, len(muxers))

opts := make([]libp2p.Option, 0, len(order))
for _, id := range order {
tpt, ok := muxers[id]
if !ok {
log.Warn("unknown or duplicate muxer in LIBP2P_MUX_PREFS: %s", id)
continue
var opts []libp2p.Option
for _, tpt := range muxers {
if enabled[tpt] {
return nil, fmt.Errorf(
"duplicate muxer found in LIBP2P_MUX_PREFS: %s",
tpt,
)
}
switch tpt {
case yamuxID:
opts = append(opts, libp2p.Muxer(tpt, yamuxTransport))
case mplexID:
opts = append(opts, libp2p.Muxer(tpt, mplex.DefaultTransport))
default:
return nil, fmt.Errorf("unknown muxer: %s", tpt)
}
}
delete(muxers, id)
opts = append(opts, libp2p.Muxer(id, tpt))
return libp2p.ChainOptions(opts...), nil
} else {
return prioritizeOptions([]priorityOption{{
priority: tptConfig.Multiplexers.Yamux,
defaultPriority: 100,
opt: libp2p.Muxer(yamuxID, yamuxTransport),
}, {
priority: tptConfig.Multiplexers.Mplex,
defaultPriority: 200,
opt: libp2p.Muxer(mplexID, mplex.DefaultTransport),
}}), nil
}

return libp2p.ChainOptions(opts...)
}

func SmuxTransport(mplex bool) func() (opts Libp2pOpts, err error) {
func SmuxTransport(tptConfig config.Transports) func() (opts Libp2pOpts, err error) {
return func() (opts Libp2pOpts, err error) {
opts.Opts = append(opts.Opts, makeSmuxTransportOption(mplex))
return
res, err := makeSmuxTransportOption(tptConfig)
opts.Opts = append(opts.Opts, res)
return opts, err
}
}
71 changes: 26 additions & 45 deletions core/node/libp2p/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,44 @@ package libp2p
import (
"fmt"

"github.com/libp2p/go-libp2p"
config "github.com/ipfs/go-ipfs-config"
libp2p "github.com/libp2p/go-libp2p"
metrics "github.com/libp2p/go-libp2p-core/metrics"
noise "github.com/libp2p/go-libp2p-noise"
libp2pquic "github.com/libp2p/go-libp2p-quic-transport"
secio "github.com/libp2p/go-libp2p-secio"
tls "github.com/libp2p/go-libp2p-tls"
tcp "github.com/libp2p/go-tcp-transport"
websocket "github.com/libp2p/go-ws-transport"

"go.uber.org/fx"
)

// default security transports for libp2p
var defaultSecurityTransports = []string{"tls", "secio", "noise"}
func Transports(tptConfig config.Transports) interface{} {
return func(pnet struct {
fx.In
Fprint PNetFingerprint `optional:"true"`
}) (opts Libp2pOpts, err error) {
privateNetworkEnabled := pnet.Fprint != nil

func Transports(pnet struct {
fx.In
Fprint PNetFingerprint `optional:"true"`
}) (opts Libp2pOpts) {
opts.Opts = append(opts.Opts, libp2p.DefaultTransports)
if pnet.Fprint == nil {
opts.Opts = append(opts.Opts, libp2p.Transport(libp2pquic.NewTransport))
}
return opts
}

func Security(enabled bool, securityTransportOverride []string) interface{} {
if !enabled {
return func() (opts Libp2pOpts) {
// TODO: shouldn't this be Errorf to guarantee visibility?
log.Warnf(`Your IPFS node has been configured to run WITHOUT ENCRYPTED CONNECTIONS.
You will not be able to connect to any nodes configured to use encrypted connections`)
opts.Opts = append(opts.Opts, libp2p.NoSecurity)
return opts
if tptConfig.Network.TCP.WithDefault(true) {
opts.Opts = append(opts.Opts, libp2p.Transport(tcp.NewTCPTransport))
}
}

securityTransports := defaultSecurityTransports
if len(securityTransportOverride) > 0 {
securityTransports = securityTransportOverride
}
if tptConfig.Network.Websocket.WithDefault(true) {
opts.Opts = append(opts.Opts, libp2p.Transport(websocket.New))
}

var libp2pOpts []libp2p.Option
for _, tpt := range securityTransports {
switch tpt {
case "tls":
libp2pOpts = append(libp2pOpts, libp2p.Security(tls.ID, tls.New))
case "secio":
libp2pOpts = append(libp2pOpts, libp2p.Security(secio.ID, secio.New))
case "noise":
libp2pOpts = append(libp2pOpts, libp2p.Security(noise.ID, noise.New))
default:
return fx.Error(fmt.Errorf("invalid security transport specified in config: %s", tpt))
if tptConfig.Network.QUIC.WithDefault(privateNetworkEnabled) {
if privateNetworkEnabled {
// QUIC was force enabled while the private network was turned on.
// Fail and tell the user.
return opts, fmt.Errorf(
"The QUIC transport does not support private networks. " +
"Please disable Swarm.Transports.Network.QUIC.",
)
}
opts.Opts = append(opts.Opts, libp2p.Transport(libp2pquic.NewTransport))
}
}

return func() (opts Libp2pOpts) {
opts.Opts = append(opts.Opts, libp2p.ChainOptions(libp2pOpts...))
return opts
return opts, nil
}
}

Expand Down

0 comments on commit 8c6c312

Please sign in to comment.