From 5a084862f93a7187772b7168e6e12388bfc6a06f Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:00:45 -0400 Subject: [PATCH 01/18] Add support for connection gating and address factory --- .gitignore | 1 + client.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++-- config.go | 6 +++ 3 files changed, 138 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2887eff..8cc36df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .history/ +.idea/ p2p_poc peer_cache.json diff --git a/client.go b/client.go index c251dc0..40489c5 100644 --- a/client.go +++ b/client.go @@ -8,6 +8,8 @@ import ( "fmt" "io" "log" + "net" + "net/http" "os" "sync" "time" @@ -21,6 +23,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/mdns" drouting "github.com/libp2p/go-libp2p/p2p/discovery/routing" + "github.com/libp2p/go-libp2p/p2p/net/conngater" "github.com/multiformats/go-multiaddr" ) @@ -70,8 +73,8 @@ func NewClient(config Config) (P2PClient, error) { hostOpts = append(hostOpts, libp2p.Identity(config.PrivateKey)) // Configure announce addresses if provided (useful for K8s) + var announceAddrs []multiaddr.Multiaddr if len(config.AnnounceAddrs) > 0 { - var announceAddrs []multiaddr.Multiaddr for _, addrStr := range config.AnnounceAddrs { maddr, err := multiaddr.NewMultiaddr(addrStr) if err != nil { @@ -87,15 +90,77 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Using custom announce addresses: %v", config.AnnounceAddrs) } + // define address factory to remove all private IPs from being broadcasted + addressFactory := func(addrs []multiaddr.Multiaddr) []multiaddr.Multiaddr { + var publicAddrs []multiaddr.Multiaddr + for _, addr := range addrs { + // if IP is not private, add it to the list + if !isPrivateIP(addr) || config.AllowPrivateIPs { + publicAddrs = append(publicAddrs, addr) + } + } + // If a user specified a broadcast IP append it here + if len(announceAddrs) > 0 { + // here we're appending the external facing multiaddr we created above to the addressFactory so it will be broadcast out when I connect to a bootstrap node. + publicAddrs = append(publicAddrs, announceAddrs...) + } + + // If we still don't have any advertisable addresses then attempt to grab it from `https://ifconfig.me/ip` + if len(publicAddrs) == 0 { + // If no public addresses are set, let's attempt to grab it publicly + // Ignore errors because we don't care if we can't find it + ifconfig, _ := GetPublicIP(context.Background()) + if len(ifconfig) > 0 { + addr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%s", ifconfig, config.Port)) + if addr != nil { + publicAddrs = append(publicAddrs, addr) + } + } + } + return publicAddrs + } + + // Create an IP filter to optionally block private network ranges from being dialed + ipFilter, err := conngater.NewBasicConnectionGater(nil) + if err != nil { + return nil, err + } + + // By default, filter private IPs + if !config.AllowPrivateIPs { + // Add private IP blocks to be filtered out + for _, cidr := range []string{ + "10.0.0.0/8", // Private network 10.0.0.0 to 10.255.255.255 + "172.16.0.0/12", // Private network 172.16.0.0 to 172.31.255.255 + "192.168.0.0/16", // Private network 192.168.0.0 to 192.168.255.255 + "127.0.0.0/16", // Local network + "100.64.0.0/10", // Shared Address Space + "169.254.0.0/16", // Link-local addresses + } { + var ipnet *net.IPNet + var err error + _, ipnet, err = net.ParseCIDR(cidr) + if err != nil { + return nil, err + } + err = ipFilter.BlockSubnet(ipnet) + if err != nil { + continue + } + } + } + // Create libp2p host hostOpts = append(hostOpts, libp2p.ListenAddrStrings( - "/ip4/0.0.0.0/tcp/0", - "/ip6/::/tcp/0", + fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", config.Port), // Listen on all interfaces + fmt.Sprintf("/ip6/::/tcp/%d", config.Port), ), libp2p.EnableNATService(), libp2p.EnableHolePunching(), libp2p.EnableRelay(), + libp2p.AddrsFactory(addressFactory), + libp2p.ConnectionGater(ipFilter), ) h, err := libp2p.New(hostOpts...) @@ -634,3 +699,66 @@ func PrivateKeyFromHex(keyHex string) (crypto.PrivKey, error) { return priv, nil } + +// Function to check if an IP address is private +func isPrivateIP(addr multiaddr.Multiaddr) bool { + ipStr, err := extractIPFromMultiaddr(addr) + if err != nil { + return false + } + ip := net.ParseIP(ipStr) + if ip == nil || ip.To4() == nil { + return false + } + + // Define private IPv4 ranges + privateRanges := []*net.IPNet{ + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)}, + {IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)}, + {IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)}, + {IP: net.ParseIP("127.0.0.0"), Mask: net.CIDRMask(8, 32)}, + } + + // Check if the IP falls into any of the private ranges + for _, r := range privateRanges { + if r.Contains(ip) { + return true + } + } + return false +} + +// Function to extract IP information from a Multiaddr +func extractIPFromMultiaddr(addr multiaddr.Multiaddr) (string, error) { + return addr.ValueForProtocol(multiaddr.P_IP4) +} + +// GetPublicIP fetches the public IP address from ifconfig.me +func GetPublicIP(ctx context.Context) (string, error) { + transport := &http.Transport{ + DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) { + // Force the use of IPv4 by specifying 'tcp4' as the network + return (&net.Dialer{}).DialContext(ctx, "tcp4", addr) + }, + TLSHandshakeTimeout: 10 * time.Second, + } + client := &http.Client{ + Transport: transport, + } + req, err := http.NewRequestWithContext(ctx, "GET", "https://ifconfig.me/ip", nil) + if err != nil { + return "", err + } + + resp, err := client.Do(req) + if err != nil { + return "", err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), resp.Body.Close() +} diff --git a/config.go b/config.go index 992524f..1c87d73 100644 --- a/config.go +++ b/config.go @@ -53,4 +53,10 @@ type Config struct { // Example: []string{"/ip4/203.0.113.1/tcp/4001"} // If not provided, libp2p will automatically detect and announce local addresses. AnnounceAddrs []string + + // AllowPrivateIPs indicates whether to allow connections to peers with private IP addresses. + AllowPrivateIPs bool + + // Port is the network port to listen on for incoming connections. If zero, a random port will be chosen. + Port int } From 5e20c5587d8d32a74d7b4680abd185f67ea4a72d Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:08:20 -0400 Subject: [PATCH 02/18] Add log message --- client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client.go b/client.go index 40489c5..7a577a1 100644 --- a/client.go +++ b/client.go @@ -117,6 +117,9 @@ func NewClient(config Config) (P2PClient, error) { } } } + if len(publicAddrs) > 0 { + logger.Infof("Using advertising addresses: %v", publicAddrs[0]) + } return publicAddrs } From a724bd28f920eecacbb641d1dccbe3b40f0aceb0 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:09:43 -0400 Subject: [PATCH 03/18] Add port to example --- example/go.mod | 4 ++-- example/main.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/example/go.mod b/example/go.mod index 5bdd7a7..e36f2a0 100644 --- a/example/go.mod +++ b/example/go.mod @@ -3,8 +3,9 @@ module github.com/ordishs/p2p_poc/example go 1.25.1 require ( - github.com/ordishs/gocore v1.0.81 github.com/bsv-blockchain/go-p2p-message-bus v0.0.0 + github.com/libp2p/go-libp2p v0.43.0 + github.com/ordishs/gocore v1.0.81 ) require ( @@ -39,7 +40,6 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect - github.com/libp2p/go-libp2p v0.43.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-kad-dht v0.34.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect diff --git a/example/main.go b/example/main.go index b855704..7e79df4 100644 --- a/example/main.go +++ b/example/main.go @@ -10,15 +10,16 @@ import ( "syscall" "time" + p2p "github.com/bsv-blockchain/go-p2p-message-bus" "github.com/libp2p/go-libp2p/core/crypto" "github.com/ordishs/gocore" - p2p "github.com/bsv-blockchain/go-p2p-message-bus" ) func main() { name := flag.String("name", "", "Your node name") privateKey := flag.String("key", "", "Private key hex (will generate if not provided)") topics := flag.String("topics", "broadcast_p2p_poc", "Comma-separated list of topics to subscribe to") + port := flag.Int("port", 0, "port to listen on (0 for random)") noBroadcast := flag.Bool("no-broadcast", false, "Disable message broadcasting") flag.Parse() @@ -65,6 +66,7 @@ func main() { Name: *name, Logger: logger, PrivateKey: privKey, + Port: port, PeerCacheFile: "peer_cache.json", // Enable peer persistence }) if err != nil { From 6c63858b96d7ff9ac89b81a54feca0e08b7f4525 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:10:43 -0400 Subject: [PATCH 04/18] Fix port in example --- example/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/main.go b/example/main.go index 7e79df4..2fa905c 100644 --- a/example/main.go +++ b/example/main.go @@ -66,7 +66,7 @@ func main() { Name: *name, Logger: logger, PrivateKey: privKey, - Port: port, + Port: *port, PeerCacheFile: "peer_cache.json", // Enable peer persistence }) if err != nil { From bd8847a79a3dca1c798c5e7bc45ed476aa9f5574 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:17:15 -0400 Subject: [PATCH 05/18] Add private ipv6 addrs --- client.go | 7 ++++++- example/main.go | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 7a577a1..4831840 100644 --- a/client.go +++ b/client.go @@ -714,12 +714,17 @@ func isPrivateIP(addr multiaddr.Multiaddr) bool { return false } - // Define private IPv4 ranges + // Define private IPv4 and IPv6 ranges privateRanges := []*net.IPNet{ + // IPv4 {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)}, {IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)}, {IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)}, {IP: net.ParseIP("127.0.0.0"), Mask: net.CIDRMask(8, 32)}, + // IPv6 + {IP: net.ParseIP("fc00::"), Mask: net.CIDRMask(7, 128)}, // Unique local address + {IP: net.ParseIP("fe80::"), Mask: net.CIDRMask(10, 128)}, // Link-local unicast + {IP: net.ParseIP("::1"), Mask: net.CIDRMask(128, 128)}, // Loopback } // Check if the IP falls into any of the private ranges diff --git a/example/main.go b/example/main.go index 2fa905c..b3dd8e4 100644 --- a/example/main.go +++ b/example/main.go @@ -63,11 +63,12 @@ func main() { // Create P2P client client, err := p2p.NewClient(p2p.Config{ - Name: *name, - Logger: logger, - PrivateKey: privKey, - Port: *port, - PeerCacheFile: "peer_cache.json", // Enable peer persistence + Name: *name, + Logger: logger, + PrivateKey: privKey, + Port: *port, + AllowPrivateIPs: false, + PeerCacheFile: "peer_cache.json", // Enable peer persistence }) if err != nil { logger.Fatalf("Failed to create P2P client: %v", err) From eeb8273b480bd0e0887d2182994ec5052a6628b3 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:23:20 -0400 Subject: [PATCH 06/18] Add logging --- client.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 4831840..6a98346 100644 --- a/client.go +++ b/client.go @@ -95,7 +95,8 @@ func NewClient(config Config) (P2PClient, error) { var publicAddrs []multiaddr.Multiaddr for _, addr := range addrs { // if IP is not private, add it to the list - if !isPrivateIP(addr) || config.AllowPrivateIPs { + logger.Infof("address is private? %v", isPrivateIP(config, addr)) + if !isPrivateIP(config, addr) || config.AllowPrivateIPs { publicAddrs = append(publicAddrs, addr) } } @@ -704,16 +705,19 @@ func PrivateKeyFromHex(keyHex string) (crypto.PrivKey, error) { } // Function to check if an IP address is private -func isPrivateIP(addr multiaddr.Multiaddr) bool { +func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { ipStr, err := extractIPFromMultiaddr(addr) if err != nil { return false } + config.Logger.Infof("Extracted IP: %s", ipStr) ip := net.ParseIP(ipStr) if ip == nil || ip.To4() == nil { return false } + config.Logger.Infof("Parsed IP: %s", ip.String()) + // Define private IPv4 and IPv6 ranges privateRanges := []*net.IPNet{ // IPv4 @@ -730,6 +734,7 @@ func isPrivateIP(addr multiaddr.Multiaddr) bool { // Check if the IP falls into any of the private ranges for _, r := range privateRanges { if r.Contains(ip) { + config.Logger.Infof("Found private IP: %s", ip) return true } } From f600d766db3058fdaba18ba0fdf0250f9450ba91 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:26:32 -0400 Subject: [PATCH 07/18] Add more loggin --- client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client.go b/client.go index 6a98346..86c23bd 100644 --- a/client.go +++ b/client.go @@ -706,6 +706,7 @@ func PrivateKeyFromHex(keyHex string) (crypto.PrivKey, error) { // Function to check if an IP address is private func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { + config.Logger.Infof("Multiaddr: %s", addr.String()) ipStr, err := extractIPFromMultiaddr(addr) if err != nil { return false From f9aae619eaa49e7218a9ac8711190eabd3902ae4 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:28:17 -0400 Subject: [PATCH 08/18] Add ipv6 support --- client.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 86c23bd..f79cf03 100644 --- a/client.go +++ b/client.go @@ -742,9 +742,13 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { return false } -// Function to extract IP information from a Multiaddr +// Function to extract IP information from a Multiaddr (supports IPv4 and IPv6) func extractIPFromMultiaddr(addr multiaddr.Multiaddr) (string, error) { - return addr.ValueForProtocol(multiaddr.P_IP4) + ip, err := addr.ValueForProtocol(multiaddr.P_IP4) + if err == nil && ip != "" { + return ip, nil + } + return addr.ValueForProtocol(multiaddr.P_IP6) } // GetPublicIP fetches the public IP address from ifconfig.me From f130dc951b7126b734eb0634e10c6244c8353231 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:32:38 -0400 Subject: [PATCH 09/18] Fix loopback --- client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index f79cf03..d676cd0 100644 --- a/client.go +++ b/client.go @@ -729,9 +729,9 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { // IPv6 {IP: net.ParseIP("fc00::"), Mask: net.CIDRMask(7, 128)}, // Unique local address {IP: net.ParseIP("fe80::"), Mask: net.CIDRMask(10, 128)}, // Link-local unicast - {IP: net.ParseIP("::1"), Mask: net.CIDRMask(128, 128)}, // Loopback + {IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128)}, // Loopback } } - + // Check if the IP falls into any of the private ranges for _, r := range privateRanges { if r.Contains(ip) { From 6817deb7e0c544b08785562a07f6157ba8d67660 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:34:12 -0400 Subject: [PATCH 10/18] fix loopback --- client.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index d676cd0..01f28b7 100644 --- a/client.go +++ b/client.go @@ -729,16 +729,19 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { // IPv6 {IP: net.ParseIP("fc00::"), Mask: net.CIDRMask(7, 128)}, // Unique local address {IP: net.ParseIP("fe80::"), Mask: net.CIDRMask(10, 128)}, // Link-local unicast - {IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128)}, // Loopback } } - - // Check if the IP falls into any of the private ranges + + // Check if the IP falls into any of the private ranges or is loopback (::1) for _, r := range privateRanges { if r.Contains(ip) { config.Logger.Infof("Found private IP: %s", ip) return true } } + if ip.Equal(net.IPv6loopback) { // ::1 + config.Logger.Infof("Found IPv6 loopback: %s", ip) + return true + } return false } From 2cbe26fada6d208ceeddebb25fd0402d2409fdf7 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:36:02 -0400 Subject: [PATCH 11/18] Add ipv6 support --- client.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client.go b/client.go index 01f28b7..fa80227 100644 --- a/client.go +++ b/client.go @@ -712,6 +712,11 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { return false } config.Logger.Infof("Extracted IP: %s", ipStr) + // Check for IPv6 loopback + if ipStr == "::1" { + return true + } + ip := net.ParseIP(ipStr) if ip == nil || ip.To4() == nil { return false @@ -738,10 +743,7 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { return true } } - if ip.Equal(net.IPv6loopback) { // ::1 - config.Logger.Infof("Found IPv6 loopback: %s", ip) - return true - } + return false } From b23fb5b165b34062b5c24734eef1182b3cf7a5af Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:38:58 -0400 Subject: [PATCH 12/18] More --- client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index fa80227..fa6dc87 100644 --- a/client.go +++ b/client.go @@ -110,8 +110,12 @@ func NewClient(config Config) (P2PClient, error) { if len(publicAddrs) == 0 { // If no public addresses are set, let's attempt to grab it publicly // Ignore errors because we don't care if we can't find it - ifconfig, _ := GetPublicIP(context.Background()) + ifconfig, err := GetPublicIP(context.Background()) + if err != nil { + logger.Infof("Failed to get public IP address: %v", err) + } if len(ifconfig) > 0 { + logger.Infof("Public IP address: %v", ifconfig) addr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%s", ifconfig, config.Port)) if addr != nil { publicAddrs = append(publicAddrs, addr) From 37dd56fc00e10d978c3fa9b42dcf0c96843dc396 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:40:57 -0400 Subject: [PATCH 13/18] err --- client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index fa6dc87..d906665 100644 --- a/client.go +++ b/client.go @@ -116,7 +116,10 @@ func NewClient(config Config) (P2PClient, error) { } if len(ifconfig) > 0 { logger.Infof("Public IP address: %v", ifconfig) - addr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%s", ifconfig, config.Port)) + addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%s", ifconfig, config.Port)) + if err != nil { + logger.Infof("Failed to create multiaddr from public IP: %v", err) + } if addr != nil { publicAddrs = append(publicAddrs, addr) } From 4448a41ff537f7a25a1527566d285f6b0a4f165a Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:41:41 -0400 Subject: [PATCH 14/18] Fix int --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index d906665..51249a0 100644 --- a/client.go +++ b/client.go @@ -116,7 +116,7 @@ func NewClient(config Config) (P2PClient, error) { } if len(ifconfig) > 0 { logger.Infof("Public IP address: %v", ifconfig) - addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%s", ifconfig, config.Port)) + addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", ifconfig, config.Port)) if err != nil { logger.Infof("Failed to create multiaddr from public IP: %v", err) } From 9d9d80cc16518fe7f26f01d2bb0049de27fbac82 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 1 Oct 2025 16:59:05 -0400 Subject: [PATCH 15/18] enable autonatv2 --- client.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/client.go b/client.go index 51249a0..ac82e98 100644 --- a/client.go +++ b/client.go @@ -95,8 +95,7 @@ func NewClient(config Config) (P2PClient, error) { var publicAddrs []multiaddr.Multiaddr for _, addr := range addrs { // if IP is not private, add it to the list - logger.Infof("address is private? %v", isPrivateIP(config, addr)) - if !isPrivateIP(config, addr) || config.AllowPrivateIPs { + if !isPrivateIP(addr) || config.AllowPrivateIPs { publicAddrs = append(publicAddrs, addr) } } @@ -115,7 +114,6 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Failed to get public IP address: %v", err) } if len(ifconfig) > 0 { - logger.Infof("Public IP address: %v", ifconfig) addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", ifconfig, config.Port)) if err != nil { logger.Infof("Failed to create multiaddr from public IP: %v", err) @@ -125,9 +123,7 @@ func NewClient(config Config) (P2PClient, error) { } } } - if len(publicAddrs) > 0 { - logger.Infof("Using advertising addresses: %v", publicAddrs[0]) - } + return publicAddrs } @@ -170,6 +166,7 @@ func NewClient(config Config) (P2PClient, error) { libp2p.EnableNATService(), libp2p.EnableHolePunching(), libp2p.EnableRelay(), + libp2p.EnableAutoNATv2(), libp2p.AddrsFactory(addressFactory), libp2p.ConnectionGater(ipFilter), ) @@ -712,13 +709,11 @@ func PrivateKeyFromHex(keyHex string) (crypto.PrivKey, error) { } // Function to check if an IP address is private -func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { - config.Logger.Infof("Multiaddr: %s", addr.String()) +func isPrivateIP(addr multiaddr.Multiaddr) bool { ipStr, err := extractIPFromMultiaddr(addr) if err != nil { return false } - config.Logger.Infof("Extracted IP: %s", ipStr) // Check for IPv6 loopback if ipStr == "::1" { return true @@ -729,8 +724,6 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { return false } - config.Logger.Infof("Parsed IP: %s", ip.String()) - // Define private IPv4 and IPv6 ranges privateRanges := []*net.IPNet{ // IPv4 @@ -746,7 +739,6 @@ func isPrivateIP(config Config, addr multiaddr.Multiaddr) bool { // Check if the IP falls into any of the private ranges or is loopback (::1) for _, r := range privateRanges { if r.Contains(ip) { - config.Logger.Infof("Found private IP: %s", ip) return true } } From 9f4ebd6e979754e77d0e3c5d4f170a4731f2743c Mon Sep 17 00:00:00 2001 From: oskarszoon <1449115+oskarszoon@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:06:02 +0200 Subject: [PATCH 16/18] Enable relay for instances stuck behind NATs in NATs in docker --- client.go | 2 +- go.mod | 4 ++++ go.sum | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index ac82e98..cbbfd9e 100644 --- a/client.go +++ b/client.go @@ -166,7 +166,7 @@ func NewClient(config Config) (P2PClient, error) { libp2p.EnableNATService(), libp2p.EnableHolePunching(), libp2p.EnableRelay(), - libp2p.EnableAutoNATv2(), + libp2p.EnableAutoRelayWithStaticRelays([]peer.AddrInfo{}), // Enable auto relay discovery via DHT libp2p.AddrsFactory(addressFactory), libp2p.ConnectionGater(ipFilter), ) diff --git a/go.mod b/go.mod index 65228bb..f3cff00 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/koron/go-ssdp v0.0.6 // indirect @@ -50,7 +51,9 @@ require ( github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/libp2p/zeroconf/v2 v2.2.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/miekg/dns v1.1.68 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect @@ -66,6 +69,7 @@ require ( github.com/multiformats/go-multistream v0.6.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/ordishs/gocore v1.0.81 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect diff --git a/go.sum b/go.sum index c843827..a96ab76 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -164,9 +166,14 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= @@ -213,6 +220,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/ordishs/gocore v1.0.81 h1:5egJfLOVrdnK6es2ED1SbXVfEazGSMkpvo1bvvKO8Nc= +github.com/ordishs/gocore v1.0.81/go.mod h1:8/PDn0aIq7/AQcrGBXHE1rkw8bkd33bwgCo2SXDq09s= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= @@ -437,6 +446,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 40c6a6ba5c68d64ebef320f30e24a9a1617b7d62 Mon Sep 17 00:00:00 2001 From: oskarszoon <1449115+oskarszoon@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:10:30 +0200 Subject: [PATCH 17/18] More NAT/Relay options --- client.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client.go b/client.go index cbbfd9e..e6d7d54 100644 --- a/client.go +++ b/client.go @@ -163,10 +163,11 @@ func NewClient(config Config) (P2PClient, error) { fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", config.Port), // Listen on all interfaces fmt.Sprintf("/ip6/::/tcp/%d", config.Port), ), - libp2p.EnableNATService(), - libp2p.EnableHolePunching(), - libp2p.EnableRelay(), - libp2p.EnableAutoRelayWithStaticRelays([]peer.AddrInfo{}), // Enable auto relay discovery via DHT + libp2p.NATPortMap(), // Try UPnP/NAT-PMP for automatic port forwarding + libp2p.EnableNATService(), // AutoNAT to detect if we're reachable + libp2p.EnableHolePunching(), // DCUtR protocol for NAT hole punching + libp2p.EnableRelay(), // Act as relay for others + libp2p.EnableAutoRelayWithStaticRelays([]peer.AddrInfo{}), // Use relays if we're unreachable libp2p.AddrsFactory(addressFactory), libp2p.ConnectionGater(ipFilter), ) From 9f0e0ad56a57bad56a47293e581cdff972601bcc Mon Sep 17 00:00:00 2001 From: oskarszoon <1449115+oskarszoon@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:21:55 +0200 Subject: [PATCH 18/18] [TEMP] Simplify back to NAT --- client.go | 123 +++++++++--------------------------------------------- 1 file changed, 20 insertions(+), 103 deletions(-) diff --git a/client.go b/client.go index e6d7d54..f214467 100644 --- a/client.go +++ b/client.go @@ -9,7 +9,6 @@ import ( "io" "log" "net" - "net/http" "os" "sync" "time" @@ -23,7 +22,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/discovery/mdns" drouting "github.com/libp2p/go-libp2p/p2p/discovery/routing" - "github.com/libp2p/go-libp2p/p2p/net/conngater" "github.com/multiformats/go-multiaddr" ) @@ -90,88 +88,37 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Using custom announce addresses: %v", config.AnnounceAddrs) } - // define address factory to remove all private IPs from being broadcasted - addressFactory := func(addrs []multiaddr.Multiaddr) []multiaddr.Multiaddr { - var publicAddrs []multiaddr.Multiaddr - for _, addr := range addrs { - // if IP is not private, add it to the list - if !isPrivateIP(addr) || config.AllowPrivateIPs { - publicAddrs = append(publicAddrs, addr) - } - } - // If a user specified a broadcast IP append it here - if len(announceAddrs) > 0 { - // here we're appending the external facing multiaddr we created above to the addressFactory so it will be broadcast out when I connect to a bootstrap node. - publicAddrs = append(publicAddrs, announceAddrs...) - } - - // If we still don't have any advertisable addresses then attempt to grab it from `https://ifconfig.me/ip` - if len(publicAddrs) == 0 { - // If no public addresses are set, let's attempt to grab it publicly - // Ignore errors because we don't care if we can't find it - ifconfig, err := GetPublicIP(context.Background()) - if err != nil { - logger.Infof("Failed to get public IP address: %v", err) - } - if len(ifconfig) > 0 { - addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/%d", ifconfig, config.Port)) - if err != nil { - logger.Infof("Failed to create multiaddr from public IP: %v", err) - } - if addr != nil { - publicAddrs = append(publicAddrs, addr) - } - } + // Simple address factory to only use custom announce addresses if provided + // Otherwise let libp2p/AutoNAT handle address detection automatically + var addressFactory func([]multiaddr.Multiaddr) []multiaddr.Multiaddr + if len(announceAddrs) > 0 { + addressFactory = func([]multiaddr.Multiaddr) []multiaddr.Multiaddr { + return announceAddrs } - - return publicAddrs + logger.Infof("Using only custom announce addresses for advertising") } - // Create an IP filter to optionally block private network ranges from being dialed - ipFilter, err := conngater.NewBasicConnectionGater(nil) - if err != nil { - return nil, err - } - - // By default, filter private IPs - if !config.AllowPrivateIPs { - // Add private IP blocks to be filtered out - for _, cidr := range []string{ - "10.0.0.0/8", // Private network 10.0.0.0 to 10.255.255.255 - "172.16.0.0/12", // Private network 172.16.0.0 to 172.31.255.255 - "192.168.0.0/16", // Private network 192.168.0.0 to 192.168.255.255 - "127.0.0.0/16", // Local network - "100.64.0.0/10", // Shared Address Space - "169.254.0.0/16", // Link-local addresses - } { - var ipnet *net.IPNet - var err error - _, ipnet, err = net.ParseCIDR(cidr) - if err != nil { - return nil, err - } - err = ipFilter.BlockSubnet(ipnet) - if err != nil { - continue - } - } - } + // Get bootstrap peers from DHT library - these can act as relays + bootstrapPeers := dht.GetDefaultBootstrapPeerAddrInfos() - // Create libp2p host + // Create libp2p host with NAT traversal options hostOpts = append(hostOpts, libp2p.ListenAddrStrings( fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", config.Port), // Listen on all interfaces fmt.Sprintf("/ip6/::/tcp/%d", config.Port), ), - libp2p.NATPortMap(), // Try UPnP/NAT-PMP for automatic port forwarding - libp2p.EnableNATService(), // AutoNAT to detect if we're reachable - libp2p.EnableHolePunching(), // DCUtR protocol for NAT hole punching - libp2p.EnableRelay(), // Act as relay for others - libp2p.EnableAutoRelayWithStaticRelays([]peer.AddrInfo{}), // Use relays if we're unreachable - libp2p.AddrsFactory(addressFactory), - libp2p.ConnectionGater(ipFilter), + libp2p.NATPortMap(), // Try UPnP/NAT-PMP for automatic port forwarding + libp2p.EnableNATService(), // AutoNAT to detect if we're reachable + libp2p.EnableHolePunching(), // DCUtR protocol for NAT hole punching + libp2p.EnableRelay(), // Act as relay for others + libp2p.EnableAutoRelayWithStaticRelays(bootstrapPeers), // Use bootstrap nodes as potential relays ) + // Only apply address factory if custom announce addresses are provided + if addressFactory != nil { + hostOpts = append(hostOpts, libp2p.AddrsFactory(addressFactory)) + } + h, err := libp2p.New(hostOpts...) if err != nil { cancel() @@ -182,7 +129,6 @@ func NewClient(config Config) (P2PClient, error) { logger.Infof("Listening on: %v", h.Addrs()) // Set up DHT with bootstrap peers - bootstrapPeers := dht.GetDefaultBootstrapPeerAddrInfos() kadDHT, err := dht.New(ctx, h, dht.Mode(dht.ModeServer), dht.BootstrapPeers(bootstrapPeers...)) if err != nil { h.Close() @@ -756,32 +702,3 @@ func extractIPFromMultiaddr(addr multiaddr.Multiaddr) (string, error) { return addr.ValueForProtocol(multiaddr.P_IP6) } -// GetPublicIP fetches the public IP address from ifconfig.me -func GetPublicIP(ctx context.Context) (string, error) { - transport := &http.Transport{ - DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) { - // Force the use of IPv4 by specifying 'tcp4' as the network - return (&net.Dialer{}).DialContext(ctx, "tcp4", addr) - }, - TLSHandshakeTimeout: 10 * time.Second, - } - client := &http.Client{ - Transport: transport, - } - req, err := http.NewRequestWithContext(ctx, "GET", "https://ifconfig.me/ip", nil) - if err != nil { - return "", err - } - - resp, err := client.Do(req) - if err != nil { - return "", err - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - return string(body), resp.Body.Close() -}