master
go 139 lines 3.98 KB
Raw
1 package commands
2
3 import (
4 "fmt"
5 "io"
6
7 cmds "github.com/ipfs/go-ipfs-cmds"
8 cmdenv "github.com/ipfs/kubo/core/commands/cmdenv"
9 "github.com/libp2p/go-libp2p/core/network"
10 ma "github.com/multiformats/go-multiaddr"
11 )
12
13 // reachabilityHost provides access to the AutoNAT reachability status.
14 type reachabilityHost interface {
15 Reachability() network.Reachability
16 }
17
18 // confirmedAddrsHost provides access to per-address reachability from AutoNAT V2.
19 type confirmedAddrsHost interface {
20 ConfirmedAddrs() (reachable, unreachable, unknown []ma.Multiaddr)
21 }
22
23 // autoNATResult represents the AutoNAT reachability information.
24 type autoNATResult struct {
25 Reachability string `json:"reachability"`
26 Reachable []string `json:"reachable,omitempty"`
27 Unreachable []string `json:"unreachable,omitempty"`
28 Unknown []string `json:"unknown,omitempty"`
29 }
30
31 func multiaddrsToStrings(addrs []ma.Multiaddr) []string {
32 out := make([]string, len(addrs))
33 for i, a := range addrs {
34 out[i] = a.String()
35 }
36 return out
37 }
38
39 func writeAddrSection(w io.Writer, label string, addrs []string) {
40 if len(addrs) > 0 {
41 fmt.Fprintf(w, " %s:\n", label)
42 for _, addr := range addrs {
43 fmt.Fprintf(w, " %s\n", addr)
44 }
45 }
46 }
47
48 var swarmAddrsAutoNATCmd = &cmds.Command{
49 Helptext: cmds.HelpText{
50 Tagline: "Show address reachability as determined by AutoNAT V2.",
51 ShortDescription: `
52 'ipfs swarm addrs autonat' shows the reachability status of your node's
53 addresses as determined by AutoNAT V2.
54 `,
55 LongDescription: `
56 'ipfs swarm addrs autonat' shows the reachability status of your node's
57 addresses as verified by AutoNAT V2.
58
59 AutoNAT V2 probes your node's addresses to determine if they are reachable
60 from the public internet. This helps understand whether other peers can
61 dial your node directly.
62
63 The output shows:
64 - Reachability: Overall status (Public, Private, or Unknown)
65 - Reachable: Addresses confirmed to be publicly reachable
66 - Unreachable: Addresses that failed reachability checks
67 - Unknown: Addresses that haven't been tested yet
68
69 For more information on AutoNAT V2, see:
70 https://github.com/libp2p/specs/blob/master/autonat/autonat-v2.md
71
72 Example:
73
74 > ipfs swarm addrs autonat
75 AutoNAT V2 Status:
76 Reachability: Public
77
78 Per-Address Reachability:
79 Reachable:
80 /ip4/203.0.113.42/tcp/4001
81 /ip4/203.0.113.42/udp/4001/quic-v1
82 Unreachable:
83 /ip6/2001:db8::1/tcp/4001
84 Unknown:
85 /ip4/203.0.113.42/udp/4001/webrtc-direct
86 `,
87 },
88 Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
89 nd, err := cmdenv.GetNode(env)
90 if err != nil {
91 return err
92 }
93
94 if !nd.IsOnline {
95 return ErrNotOnline
96 }
97
98 result := autoNATResult{
99 Reachability: network.ReachabilityUnknown.String(),
100 }
101
102 // Get per-address reachability from AutoNAT V2.
103 // The host embeds *BasicHost (closableBasicHost, closableRoutedHost)
104 // which implements ConfirmedAddrs.
105 if h, ok := nd.PeerHost.(confirmedAddrsHost); ok {
106 reachable, unreachable, unknown := h.ConfirmedAddrs()
107 result.Reachable = multiaddrsToStrings(reachable)
108 result.Unreachable = multiaddrsToStrings(unreachable)
109 result.Unknown = multiaddrsToStrings(unknown)
110 }
111
112 // Get overall reachability status.
113 if h, ok := nd.PeerHost.(reachabilityHost); ok {
114 result.Reachability = h.Reachability().String()
115 }
116
117 return cmds.EmitOnce(res, result)
118 },
119 Type: autoNATResult{},
120 Encoders: cmds.EncoderMap{
121 cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, result autoNATResult) error {
122 fmt.Fprintln(w, "AutoNAT V2 Status:")
123 fmt.Fprintf(w, " Reachability: %s\n", result.Reachability)
124
125 fmt.Fprintln(w)
126 fmt.Fprintln(w, "Per-Address Reachability:")
127
128 writeAddrSection(w, "Reachable", result.Reachable)
129 writeAddrSection(w, "Unreachable", result.Unreachable)
130 writeAddrSection(w, "Unknown", result.Unknown)
131
132 if len(result.Reachable) == 0 && len(result.Unreachable) == 0 && len(result.Unknown) == 0 {
133 fmt.Fprintln(w, " (no address reachability data available)")
134 }
135
136 return nil
137 }),
138 },
139 }