@@ -331,6 +331,33 @@ func (w *tLogWriter) Write(p []byte) (int, error) {
331331 return len (p ), nil
332332}
333333
334+ // nonLoopbackIPv4 returns the first non-loopback IPv4 address found on the host.
335+ func nonLoopbackIPv4 () (string , error ) {
336+ ifaces , err := net .Interfaces ()
337+ if err != nil {
338+ return "" , err
339+ }
340+ for _ , iface := range ifaces {
341+ if iface .Flags & net .FlagLoopback != 0 || iface .Flags & net .FlagUp == 0 {
342+ continue
343+ }
344+ addrs , err := iface .Addrs ()
345+ if err != nil {
346+ continue
347+ }
348+ for _ , addr := range addrs {
349+ ipNet , ok := addr .(* net.IPNet )
350+ if ! ok {
351+ continue
352+ }
353+ if ip4 := ipNet .IP .To4 (); ip4 != nil {
354+ return ip4 .String (), nil
355+ }
356+ }
357+ }
358+ return "" , fmt .Errorf ("no non-loopback IPv4 address found" )
359+ }
360+
334361func allocateAvailablePort (proto string ) (int , error ) {
335362 const loopback = "127.0.0.1:0"
336363 switch proto {
@@ -452,6 +479,12 @@ func testTCPTransparentWithPID(t *testing.T, d port.ParentDriver, childPID int)
452479 ensureDeps (t , "nsenter" )
453480 const childPort = 80
454481
482+ parentIP , err := nonLoopbackIPv4 ()
483+ if err != nil {
484+ t .Skip ("no non-loopback IPv4 address available: " , err )
485+ }
486+ t .Logf ("using non-loopback parent IP: %s" , parentIP )
487+
455488 // Start parent driver
456489 initComplete := make (chan struct {})
457490 quit := make (chan struct {})
@@ -524,7 +557,7 @@ func testTCPTransparentWithPID(t *testing.T, d port.ParentDriver, childPID int)
524557 portStatus , err = d .AddPort (context .TODO (),
525558 port.Spec {
526559 Proto : "tcp" ,
527- ParentIP : "127.0.0.1" ,
560+ ParentIP : parentIP ,
528561 ParentPort : parentPort ,
529562 ChildPort : childPort ,
530563 })
@@ -545,7 +578,7 @@ func testTCPTransparentWithPID(t *testing.T, d port.ParentDriver, childPID int)
545578 var conn net.Conn
546579 for i := 0 ; i < 5 ; i ++ {
547580 var dialer net.Dialer
548- conn , err = dialer .Dial ("tcp" , fmt . Sprintf ( "127.0.0.1:%d" , parentPort ))
581+ conn , err = dialer .Dial ("tcp" , net . JoinHostPort ( parentIP , strconv . Itoa ( parentPort ) ))
549582 if err == nil {
550583 break
551584 }
@@ -577,10 +610,8 @@ func testTCPTransparentWithPID(t *testing.T, d port.ParentDriver, childPID int)
577610 conn .Close ()
578611 echoCmd .Wait ()
579612
580- // Parse and verify: the echo server should see the client's IP.
581- // Port preservation only works with non-loopback source IPs (via IP_TRANSPARENT),
582- // so we only verify the IP here. With loopback, transparent dial is skipped
583- // (martian filtering blocks 127.0.0.1 arriving on non-loopback interfaces).
613+ // Parse and verify: the echo server should see the client's non-loopback IP,
614+ // not 127.0.0.1 or a hard-coded router address.
584615 clientHost , _ , err := net .SplitHostPort (clientAddr )
585616 if err != nil {
586617 t .Fatalf ("failed to parse client address %q: %v" , clientAddr , err )
0 commit comments