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

feat: basic ipfs network diagnostics #7068

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func TestCommands(t *testing.T) {
"/diag/cmds",
"/diag/cmds/clear",
"/diag/cmds/set-time",
"/diag/net",
"/diag/sys",
"/dns",
"/file",
Expand Down
1 change: 1 addition & 0 deletions core/commands/diag.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ var DiagCmd = &cmds.Command{
Subcommands: map[string]*cmds.Command{
"sys": sysDiagCmd,
"cmds": ActiveReqsCmd,
"net": netDiag,
},
}
145 changes: 145 additions & 0 deletions core/commands/netdiag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package commands

import (
"fmt"
"io"
"text/template"

"github.com/ipfs/go-ipfs/core/commands/cmdenv"
"github.com/ipfs/go-ipfs/doctor"

cmds "github.com/ipfs/go-ipfs-cmds"

libp2pNetwork "github.com/libp2p/go-libp2p-core/network"
)

type NetworkDiagnostics struct {
doctor.Status
IsOnline bool
}

const netDiagPublicText = `
You are online and reachable from the public network.
`

const netDiagUnknownText = `
IPFS has not yet determined if your node is reachable from the public network.
`

// TODO Information about Hole Punching based on NAT Type
const netDiagPrivateText = `
{{ if .UsingRelays -}}
Your IPFS node appears to be behind a Firewall or NAT and can only be reached
through a relay.
{{- else -}}
{{if .AutoRelayEnabled -}}
Your IPFS node appears to be behind a Firewall or NAT and will try to
automatically find a relay. However, it is not currently reachable.
{{- else -}}
Your IPFS node appears to be behind a Firewall or NAT and cannot be reached by
nodes in the public network.

To join the network via a relay, please enable the "auto-relay" feature:

ipfs config --bool Swarm.EnableAutoRelay true

And then restart IPFS.
{{- end -}}
{{end}}

Please note, relays are public and highly constrained resources.

For the best performance, please enable Universal Plug n Play (UPnP) on your
router so IPFS can automatically configure it to open an inbound port.

{{ if or .TCPPorts .UDPPorts -}}
Alternatively, you can manually configure your router. Please forward the following ports:

{{ if .TCPPorts -}}
TCP:{{ range $element := .TCPPorts}} {{$element}}{{end}}
{{- end}}{{if .UDPPorts -}}
UDP:{{ range $element := .UDPPorts}} {{$element}}{{end}}
{{- end}}

To (your current IP): {{.LocalIP}}
{{- end}}
{{if .Gateway -}}
If you are on a personal home network, your router's administrative console can likely be found at:
{{.Gateway}}
{{end -}}

{{if .UDPNATDeviceType -}}

Your NAT Device Type for UDP is:
{{.UDPNATDeviceType}}
{{end -}}

{{if .TCPNATDeviceType -}}

Your NAT Device Type for TCP is:
{{.TCPNATDeviceType}}
{{end -}}
`

var netDiagPublicTemplate, netDiagPrivateTemplate, netDiagUnknownTemplate *template.Template

func init() {
netDiagPublicTemplate = template.Must(template.New("net-diag-public").Parse(netDiagPublicText))
netDiagPrivateTemplate = template.Must(template.New("net-diag-private").Parse(netDiagPrivateText))
netDiagUnknownTemplate = template.Must(template.New("net-diag-unknown").Parse(netDiagUnknownText))
}

var netDiag = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Diagnose networking issues.",
ShortDescription: `
Diagnoses networking issues.
`,
LongDescription: `
Diagnoses networking issues.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
nd, err := cmdenv.GetNode(env)
if err != nil {
return err
}

if !nd.IsOnline {
return cmds.EmitOnce(res, &NetworkDiagnostics{IsOnline: false})
}

status, err := nd.Doctor.GetStatus(req.Context)
if err := cmds.EmitOnce(res, &NetworkDiagnostics{IsOnline: true, Status: *status}); err != nil {
return err
}
return err
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *NetworkDiagnostics) error {
if !out.IsOnline {
fmt.Fprintf(w, "Your node is running in offline mode.\n")
return nil
}

if !out.Listening {
fmt.Fprintf(w, "You have configured your node to not listen on any network addresses.\n")
return nil
}

var tmpl *template.Template
switch out.Reachability {
case libp2pNetwork.ReachabilityPrivate:
tmpl = netDiagPrivateTemplate
case libp2pNetwork.ReachabilityPublic:
tmpl = netDiagPublicTemplate
case libp2pNetwork.ReachabilityUnknown:
tmpl = netDiagUnknownTemplate
default:
return fmt.Errorf("unknown reachability: %d", out.Reachability)
}
return tmpl.Execute(w, &out.Status)
}),
},
Type: NetworkDiagnostics{},
}
3 changes: 3 additions & 0 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/ipfs/go-ipfs/core/bootstrap"
"github.com/ipfs/go-ipfs/core/node"
"github.com/ipfs/go-ipfs/core/node/libp2p"
"github.com/ipfs/go-ipfs/doctor"
"github.com/ipfs/go-ipfs/fuse/mount"
"github.com/ipfs/go-ipfs/namesys"
ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
Expand Down Expand Up @@ -99,6 +100,8 @@ type IpfsNode struct {
DHT *ddht.DHT `optional:"true"`
P2P *p2p.P2P `optional:"true"`

Doctor *doctor.Doctor `optional:"true"`

Process goprocess.Process
ctx context.Context

Expand Down
25 changes: 25 additions & 0 deletions core/node/doctor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package node

import (
"context"
"os"

"go.uber.org/fx"

"github.com/ipfs/go-ipfs-config"
"github.com/ipfs/go-ipfs/doctor"
libp2p "github.com/libp2p/go-libp2p-core"
)

func Doctor(lc fx.Lifecycle, host libp2p.Host, cfg *config.Config) *doctor.Doctor {
doctor := doctor.NewDoctor(host, cfg, os.Stdout)
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
return doctor.Start()
},
OnStop: func(ctx context.Context) error {
return doctor.Close()
},
})
return doctor
}
1 change: 1 addition & 0 deletions core/node/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option {
shouldBitswapProvide := !cfg.Experimental.StrategicProviding

return fx.Options(
fx.Provide(Doctor),
fx.Provide(OnlineExchange(shouldBitswapProvide)),
maybeProvide(Graphsync, cfg.Experimental.GraphsyncEnabled),
fx.Provide(Namesys(ipnsCacheSize)),
Expand Down
Loading