@@ -36,6 +36,9 @@ func Main(m *testing.M, cf func() port.ChildDriver) {
3636 case "echoserver" :
3737 runEchoServer ()
3838 os .Exit (0 )
39+ case "udpechoserver" :
40+ runUDPEchoServer ()
41+ os .Exit (0 )
3942 default :
4043 panic (fmt .Errorf ("unknown mode: %q" , mode ))
4144 }
@@ -504,14 +507,32 @@ func transparentTCPDialAndSend(t *testing.T, parentAddr string) string {
504507 return clientAddr
505508}
506509
510+ func transparentUDPDialAndSend (t * testing.T , parentAddr string ) string {
511+ conn , err := net .Dial ("udp" , parentAddr )
512+ if err != nil {
513+ t .Fatal (err )
514+ }
515+ clientAddr := conn .LocalAddr ().String ()
516+ if _ , err := conn .Write ([]byte ("hello" )); err != nil {
517+ t .Fatal (err )
518+ }
519+ conn .Close ()
520+ return clientAddr
521+ }
522+
507523func testTransparentWithPID (t * testing.T , proto string , d port.ParentDriver , childPID int ) {
508524 ensureDeps (t , "nsenter" )
509525 const childPort = 80
510526
511527 var dialAndSend transparentDialAndSend
528+ var echoMode string
512529 switch proto {
513530 case "tcp" :
514531 dialAndSend = transparentTCPDialAndSend
532+ echoMode = "echoserver"
533+ case "udp" :
534+ dialAndSend = transparentUDPDialAndSend
535+ echoMode = "udpechoserver"
515536 default :
516537 t .Fatalf ("unsupported proto for transparent test: %s" , proto )
517538 }
@@ -560,7 +581,7 @@ func testTransparentWithPID(t *testing.T, proto string, d port.ParentDriver, chi
560581 "-t" , strconv .Itoa (childPID ),
561582 exe )
562583 echoCmd .Env = append ([]string {
563- reexecKeyMode + "=echoserver" ,
584+ reexecKeyMode + "=" + echoMode ,
564585 reexecKeyEchoPort + "=" + strconv .Itoa (childPort ),
565586 }, os .Environ ()... )
566587 echoCmd .Stdout = stdoutW
@@ -611,6 +632,11 @@ func testTransparentWithPID(t *testing.T, proto string, d port.ParentDriver, chi
611632 }
612633 t .Logf ("opened port: %+v" , portStatus )
613634
635+ if proto == "udp" {
636+ // UDP dial does not return an error even if the proxy is not ready yet
637+ time .Sleep (500 * time .Millisecond )
638+ }
639+
614640 // Dial and send data
615641 parentAddr := net .JoinHostPort (parentIP , strconv .Itoa (parentPort ))
616642 clientAddr := dialAndSend (t , parentAddr )
@@ -650,3 +676,89 @@ func testTransparentWithPID(t *testing.T, proto string, d port.ParentDriver, chi
650676 t .Fatal (err )
651677 }
652678}
679+
680+ // runUDPEchoServer is a re-exec mode that runs a minimal UDP server.
681+ // It listens on 127.0.0.1:<port>, signals readiness by closing fd 3,
682+ // receives one datagram, writes the remote address to stdout, and echoes the data back.
683+ func runUDPEchoServer () {
684+ portStr := os .Getenv (reexecKeyEchoPort )
685+ if portStr == "" {
686+ panic ("udpechoserver: missing " + reexecKeyEchoPort )
687+ }
688+ addr , err := net .ResolveUDPAddr ("udp" , "127.0.0.1:" + portStr )
689+ if err != nil {
690+ panic (fmt .Errorf ("udpechoserver: resolve: %w" , err ))
691+ }
692+ conn , err := net .ListenUDP ("udp" , addr )
693+ if err != nil {
694+ panic (fmt .Errorf ("udpechoserver: listen: %w" , err ))
695+ }
696+ defer conn .Close ()
697+ // Signal readiness by closing fd 3
698+ readyW := os .NewFile (3 , "ready" )
699+ readyW .Close ()
700+
701+ buf := make ([]byte , 65507 )
702+ n , from , err := conn .ReadFromUDP (buf )
703+ if err != nil {
704+ panic (fmt .Errorf ("udpechoserver: read: %w" , err ))
705+ }
706+ fmt .Fprintln (os .Stdout , from .String ())
707+ conn .WriteToUDP (buf [:n ], from )
708+ }
709+
710+ func RunUDPTransparent (t * testing.T , pf func () port.ParentDriver ) {
711+ t .Run ("TestUDPTransparent" , func (t * testing.T ) { TestUDPTransparent (t , pf ()) })
712+ }
713+
714+ func TestUDPTransparent (t * testing.T , d port.ParentDriver ) {
715+ ensureDeps (t , "nsenter" )
716+ t .Logf ("creating USER+NET namespace" )
717+ opaque := d .OpaqueForChild ()
718+ opaqueJSON , err := json .Marshal (opaque )
719+ if err != nil {
720+ t .Fatal (err )
721+ }
722+ pr , pw , err := os .Pipe ()
723+ if err != nil {
724+ t .Fatal (err )
725+ }
726+ cmd := exec .Command ("/proc/self/exe" )
727+ cmd .Stdout = os .Stderr
728+ cmd .Stderr = os .Stderr
729+ cmd .Env = append ([]string {
730+ reexecKeyMode + "=child" ,
731+ reexecKeyOpaque + "=" + string (opaqueJSON ),
732+ reexecKeyQuitFD + "=3" }, os .Environ ()... )
733+ cmd .SysProcAttr = & syscall.SysProcAttr {
734+ Pdeathsig : syscall .SIGKILL ,
735+ Cloneflags : syscall .CLONE_NEWUSER | syscall .CLONE_NEWNET ,
736+ UidMappings : []syscall.SysProcIDMap {
737+ {
738+ ContainerID : 0 ,
739+ HostID : os .Geteuid (),
740+ Size : 1 ,
741+ },
742+ },
743+ GidMappings : []syscall.SysProcIDMap {
744+ {
745+ ContainerID : 0 ,
746+ HostID : os .Getegid (),
747+ Size : 1 ,
748+ },
749+ },
750+ }
751+ cmd .ExtraFiles = []* os.File {pr }
752+ if err := cmd .Start (); err != nil {
753+ t .Fatal (err )
754+ }
755+ defer func () {
756+ pw .Close ()
757+ cmd .Wait ()
758+ }()
759+ childPID := cmd .Process .Pid
760+ if out , err := nsenterExec (childPID , "ip" , "link" , "set" , "lo" , "up" ); err != nil {
761+ t .Fatalf ("%v, out=%s" , err , string (out ))
762+ }
763+ testTransparentWithPID (t , "udp" , d , childPID )
764+ }
0 commit comments