Skip to content

Commit f551b7d

Browse files
committed
portfwd: create separate gRPC streams for each UDP client
The UDP port forwarder previously used a single gRPC stream for all clients, which could cause responses from the guest to be sent to the wrong client on the host. This occurred because the stream was created before client connections were demultiplexed by `gvisor-tap-vsock`'s `UDPProxy`. The root cause is the interaction with `gvisor-tap-vsock`'s `UDPProxy`, which handles client demultiplexing internally based on the source address of incoming datagrams. It expects its `dialer` function to return a new `net.Conn` for each new client it detects. This commit moves the gRPC stream creation into the `UDPProxy` dialer function. This ensures a new, dedicated stream is created for each new client, fixing the incorrect response routing.
1 parent c841df5 commit f551b7d

File tree

1 file changed

+18
-15
lines changed

1 file changed

+18
-15
lines changed

pkg/portfwd/client.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"fmt"
99
"net"
10+
"sync/atomic"
1011
"time"
1112

1213
"github.com/containers/gvisor-tap-vsock/pkg/services/forwarder"
@@ -40,33 +41,35 @@ func HandleTCPConnection(ctx context.Context, client *guestagentclient.GuestAgen
4041
}
4142

4243
func HandleUDPConnection(ctx context.Context, client *guestagentclient.GuestAgentClient, conn net.PacketConn, guestAddr string) {
43-
id := fmt.Sprintf("udp-%s", conn.LocalAddr().String())
44-
45-
stream, err := client.Tunnel(ctx)
46-
if err != nil {
47-
logrus.Errorf("could not open udp tunnel for id: %s error:%v", id, err)
48-
return
49-
}
50-
51-
// Handshake message to start tunnel
52-
if err := stream.Send(&api.TunnelMessage{Id: id, Protocol: "udp", GuestAddr: guestAddr}); err != nil {
53-
logrus.Errorf("could not start udp tunnel for id: %s error:%v", id, err)
54-
return
55-
}
44+
var udpConnectionCounter uint64
45+
initialID := fmt.Sprintf("udp-%s", conn.LocalAddr().String())
5646

47+
// gvisor-tap-vsock's UDPProxy demultiplexes client connections internally based on their source address.
48+
// It calls this dialer function only when it receives a datagram from a new, unrecognized client.
49+
// For each new client, we must return a new net.Conn, which in our case is a new gRPC stream.
50+
// The atomic counter ensures that each stream has a unique ID to distinguish them on the server side.
5751
proxy, err := forwarder.NewUDPProxy(conn, func() (net.Conn, error) {
52+
id := fmt.Sprintf("%s-%d", initialID, atomic.AddUint64(&udpConnectionCounter, 1))
53+
stream, err := client.Tunnel(ctx)
54+
if err != nil {
55+
return nil, fmt.Errorf("could not open udp tunnel for id: %s error:%w", id, err)
56+
}
57+
// Handshake message to start tunnel
58+
if err := stream.Send(&api.TunnelMessage{Id: id, Protocol: "udp", GuestAddr: guestAddr}); err != nil {
59+
return nil, fmt.Errorf("could not start udp tunnel for id: %s error:%w", id, err)
60+
}
5861
rw := &GrpcClientRW{stream: stream, id: id, addr: guestAddr, protocol: "udp"}
5962
return rw, nil
6063
})
6164
if err != nil {
62-
logrus.Errorf("error in udp tunnel proxy for id: %s error:%v", id, err)
65+
logrus.Errorf("error in udp tunnel proxy for id: %s error:%v", initialID, err)
6366
return
6467
}
6568

6669
defer func() {
6770
err := proxy.Close()
6871
if err != nil {
69-
logrus.Errorf("error in closing udp tunnel proxy for id: %s error:%v", id, err)
72+
logrus.Errorf("error in closing udp tunnel proxy for id: %s error:%v", initialID, err)
7073
}
7174
}()
7275
proxy.Run()

0 commit comments

Comments
 (0)