@@ -11,6 +11,7 @@ import (
1111 "github.com/libp2p/go-libp2p"
1212 dht "github.com/libp2p/go-libp2p-kad-dht"
1313 "github.com/libp2p/go-libp2p/core/crypto"
14+ "github.com/libp2p/go-libp2p/core/event"
1415 "github.com/libp2p/go-libp2p/core/host"
1516 "github.com/libp2p/go-libp2p/core/network"
1617 peerstore "github.com/libp2p/go-libp2p/core/peer"
@@ -109,6 +110,8 @@ func StartNode(innerConfig utils.InnerConfig, pk crypto.PrivKey, tcpPort string,
109110 libp2p .Transport (quic .NewTransport ),
110111 libp2p .Transport (tcp .NewTCPTransport ),
111112 libp2p .NATPortMap (),
113+ libp2p .EnableAutoNATv2 (),
114+ libp2p .EnableHolePunching (),
112115 libp2p .Routing (func (h host.Host ) (routing.PeerRouting , error ) {
113116 // Configure DHT with options to limit connection usage
114117 idht , err = dht .New (ctx , h ,
@@ -167,9 +170,52 @@ func StartNode(innerConfig utils.InnerConfig, pk crypto.PrivKey, tcpPort string,
167170 // Refresh the routing table to help discover peers
168171 // idht.RefreshRoutingTable()
169172
173+ // Start monitoring NAT status and hole punching events
174+ go monitorNATStatus (ctx , node )
175+
170176 return node , idht , err
171177}
172178
179+ // logVPNConnectionType logs the connection method when a VPN stream is established
180+ func logVPNConnectionType (node host.Host , conn network.Conn ) {
181+ remoteAddr := conn .RemoteMultiaddr ().String ()
182+ localAddr := conn .LocalMultiaddr ().String ()
183+ peerID := conn .RemotePeer ().ShortString ()
184+ direction := conn .Stat ().Direction
185+
186+ // Determine connection type
187+ if strings .Contains (remoteAddr , "/p2p-circuit/" ) {
188+ utils .HolePunchLog .Info ("🔄 VPN over RELAYED connection with peer %s" , peerID )
189+ utils .HolePunchLog .Debug (" └─ via circuit relay: %s" , remoteAddr )
190+ utils .HolePunchLog .Warn (" └─ ⚠ Performance may be degraded (using relay)" )
191+ } else if strings .Contains (remoteAddr , "/quic" ) {
192+ if direction == network .DirInbound {
193+ utils .HolePunchLog .Success ("✓ VPN over DIRECT inbound QUIC from peer %s" , peerID )
194+ utils .HolePunchLog .Debug (" └─ Peer connected to us (hole punch success or we're public)" )
195+ } else {
196+ utils .HolePunchLog .Success ("✓ VPN over DIRECT outbound QUIC to peer %s" , peerID )
197+ }
198+ utils .HolePunchLog .Debug (" └─ Local: %s" , localAddr )
199+ utils .HolePunchLog .Debug (" └─ Remote: %s" , remoteAddr )
200+ } else if strings .Contains (remoteAddr , "/tcp" ) {
201+ if direction == network .DirInbound {
202+ utils .HolePunchLog .Success ("✓ VPN over DIRECT inbound TCP from peer %s" , peerID )
203+ } else {
204+ utils .HolePunchLog .Success ("✓ VPN over DIRECT outbound TCP to peer %s" , peerID )
205+ }
206+ utils .HolePunchLog .Debug (" └─ Local: %s" , localAddr )
207+ utils .HolePunchLog .Debug (" └─ Remote: %s" , remoteAddr )
208+ } else {
209+ utils .HolePunchLog .Info ("VPN connection established with peer %s" , peerID )
210+ utils .HolePunchLog .Debug (" └─ Type: %s" , remoteAddr )
211+ }
212+
213+ // Check if connection is limited by resource manager
214+ if conn .Stat ().Limited {
215+ utils .HolePunchLog .Warn (" └─ ⚠ VPN connection is RATE LIMITED by resource manager" )
216+ }
217+ }
218+
173219func shouldCloseStream (err error ) bool {
174220 if err == nil {
175221 return false
@@ -224,6 +270,9 @@ func makeStreamHandler(node host.Host) network.StreamHandler {
224270 streamLog .Info ("Starting stream handler for peer: %s" , peerID )
225271 streamLog .Debug ("Node status: %v (true=server, false=client)" , utils .IS_NODE_HOST )
226272
273+ // Log VPN connection type (NAT/hole punching info)
274+ logVPNConnectionType (node , s .Conn ())
275+
227276 // CRITICAL: Protect this VPN connection from being pruned by the connection manager
228277 // This prevents the connection manager from closing VPN streams when trimming connections
229278 streamLog .Info ("🛡️ Protecting VPN connection for peer %s" , peerID )
@@ -379,3 +428,71 @@ func makeStreamHandler(node host.Host) network.StreamHandler {
379428 }()
380429 } // end of stream handler function
381430} // end of makeStreamHandler
431+
432+ // monitorNATStatus monitors and logs AutoNAT reachability status
433+ func monitorNATStatus (ctx context.Context , node host.Host ) {
434+ // Subscribe to AutoNAT reachability events
435+ sub , err := node .EventBus ().Subscribe (new (event.EvtPeerIdentificationCompleted ))
436+ if err != nil {
437+ utils .NATLog .Error ("Failed to subscribe to AutoNAT events: %v" , err )
438+ return
439+ }
440+ defer sub .Close ()
441+
442+ utils .NATLog .Info ("AutoNAT monitoring started" )
443+
444+ // Check initial reachability status periodically
445+ ticker := time .NewTicker (30 * time .Second )
446+ defer ticker .Stop ()
447+
448+ checkReachability := func () {
449+ conns := node .Network ().Conns ()
450+ if len (conns ) == 0 {
451+ utils .NATLog .Debug ("No connections yet, reachability unknown" )
452+ return
453+ }
454+
455+ // Check if we have any public addresses
456+ hasPublicAddr := false
457+ for _ , addr := range node .Addrs () {
458+ addrStr := addr .String ()
459+ // Check if address is not a private IP
460+ if ! strings .Contains (addrStr , "127.0.0.1" ) &&
461+ ! strings .Contains (addrStr , "192.168." ) &&
462+ ! strings .Contains (addrStr , "10." ) &&
463+ ! strings .Contains (addrStr , "172.16." ) &&
464+ ! strings .Contains (addrStr , "172.17." ) &&
465+ ! strings .Contains (addrStr , "172.18." ) &&
466+ ! strings .Contains (addrStr , "172.19." ) &&
467+ ! strings .Contains (addrStr , "172.2" ) &&
468+ ! strings .Contains (addrStr , "172.30." ) &&
469+ ! strings .Contains (addrStr , "172.31." ) {
470+ hasPublicAddr = true
471+ utils .NATLog .Debug ("Public address detected: %s" , addrStr )
472+ }
473+ }
474+
475+ if hasPublicAddr {
476+ utils .NATLog .Success ("✓ Node appears PUBLICLY reachable (has public addresses)" )
477+ } else {
478+ utils .NATLog .Warn ("⚠ Node appears behind NAT/firewall (only private addresses)" )
479+ utils .NATLog .Info ("Hole punching or relay will be used for incoming connections" )
480+ }
481+ }
482+
483+ // Initial check after 5 seconds
484+ time .Sleep (5 * time .Second )
485+ checkReachability ()
486+
487+ for {
488+ select {
489+ case <- ctx .Done ():
490+ return
491+ case <- ticker .C :
492+ // Periodic reachability check
493+ checkReachability ()
494+ case evt := <- sub .Out ():
495+ _ = evt // Event received, will trigger reachability check on next ticker
496+ }
497+ }
498+ }
0 commit comments