あどけない話

インターネットに関する技術的な話など

Migration API for QUIC clients

If I understand correctly, most QUIC implementations of clients and servers uses unconnected UDP sockets with sendto()/sendmsg() and recvfrom()/recvmsg(). For the server side, this is probably because they adopt event-driven programming. The event loop calls recvfrom()/recvmsg() and dispatches a received packet according to the peer address.

As I explained in "Implementation status of QUIC in Haskell", the quic library in Haskell made use of connected sockets in both the client and server sides. Perhaps, this is a good thing for the server side first because lightweight thread programming is common in Haskell and second because dispatching is done in the kernel.

But what about the client side? Since RFCs relating to QUIC are published, I'm trying to fix API of the quic library for the first official release. What kind of migration API should be provided for clients?

Let's consider this typical scenario:

  1. A QUIC client is using a 5G network.
  2. The client moves to the place where WiFi is available.
  3. The client migrates the connection from the 5G network to the WiFi network.

If the clients uses a connected socket, this migration can be implemented as follows:

  1. The client needs to detect the event that the WiFi network interface is available.
  2. The client creates a new connected socket. When connect() is called, the kernel sets the remote address/port according to the argument. Then it looks up the routing table with the server's IP address. Since the WiFi network interface is resolved, the kernel sets the local address of the socket to the IP address of the network interface. A local port number is chosen randomly.
  3. The client starts sending packets through the new socket with send().

It's easy for the quic library to provide the API for item 2. But how to implement item 1? Do major OSes provide such API for network interfaces? Should we prepare a watch-dog thread for network interface events?

I have been wondering why other implementors do not talk about this issue. I finally realized that I went the wrong way. That is, unconnected socket should be used in the client side. If sendto()/sendmsg() is used with a unconnected socket, connection migration is done automatically:

  • When sendto()/sendmsg() is called, the kernel sets the destination address/port of the packet according to the argument. Then it looks up the routing table with the server's IP address. Since a proper network interface is resolved, the kernel sets the source address of the packet to the IP address of the network interface. A local port number is chosen randomly at the first sendto()/sendmsg().

Conclusion: connection migration can be done without any specific API.