Skip to content

snet: support for multihomed socket#4929

Open
juagargi wants to merge 23 commits into
scionproto:masterfrom
juagargi:multihomed
Open

snet: support for multihomed socket#4929
juagargi wants to merge 23 commits into
scionproto:masterfrom
juagargi:multihomed

Conversation

@juagargi
Copy link
Copy Markdown
Contributor

@juagargi juagargi commented May 22, 2026

This PR adds a basic multihomed support for sockets created using snet.
It allows to create SCION snet sockets without binding them to a local address.
The changes should not affect any previous code that created sockets binding them to a local address.
Two use cases are relevant, and used as examples:

  • A client opens a socket to send a message to a given remote, not needing to bind the socket to any interface/address in particular.
  • A server opens a socket to listen to any connection, not bound to any interface/address in particular.

In the first case, the local address is found via (a syscall to the kernel) creating a regular UDP socket with the specified destination, letting the kernel check the routing table and deciding what local IP address the socket will have.

In the second case, the packet received contains the last hop, which is used to create a regular UDP socket, similar to the case above. This time, the socket is not bound to the local address, and the local address is used only to allow typical constructs in the server such as

// Read data from the client.
n, remoteAddr, err := conn.ReadFrom(buff)
...
// Reply to the client.
n, err = conn.WriteTo([]byte(buff), remoteAddr)

In both cases no network traffic is created, and only the syscalls are needed to communicate with the kernel.

Additionally, there is a mechanism that reduces the amount of syscalls needed to open an unbound snet socket, and it is based on caching previous syscalls to the kernel.
If we denote outbound(remote) the function that returns the local IP address for a given remote address (the next hop of a SCION path), we keep a map of these results. If the cached map doesn't contain an entry for a given remote, the regular outbound(remote) is executed, thus issuing syscalls.

The cache is valid only for as long as the local addresses do not change, or there is a change in the routing table. We assume that a change of the routing table will be always accompanied by a change on the local interfaces' configuration, be it in the form of their local addresses, or creation/removal of some of these interfaces. If such an event is detected, the whole cache is cleared.

The detection of the changes should be done via e.g. netlink, but this PR is not using it, instead relying on the much simpler approach of querying the local interface state every T seconds, where T is defined here as 1 second.
This implies that in the case of a change of interface local address, or creation/deletion of an interface, there could be traffic being sent using a wrong local address (for at most T seconds).

This PR enables binding of SCION sockets to unspecified IP, with the following uncovered cases (i.e., clients/servers that rely on this feature may lose connectivity, drop packets):

  • Routing changes on the endhost that do not imply interface changes.
  • In case of a change of interface local address, or creation/deletion of an interface, there could be traffic being sent using a wrong local address (for at most T seconds).

In order to tackle this shortcoming one should implement a different solution using e.g. netlink.

In the case of using a dispatcher to obtain or send the packets, although the snet socket would NOT require to be unbound to any local address, this PR should not introduce any visible effects.

An acceptance test is provided in acceptance/multihomed.

Original PR preview was juagargi#53


This change is Reviewable

juagargi added 23 commits May 22, 2026 12:08
Via the creation of a UDP socket with destination the next hop,
those snet sockets that were created without binding them to a local
address, can now be used regularly.
There is a additional cache that allows skipping the UDP socket creation
if the address to dial to has been previously seen.
There is a recurrent refresh function running in the background that
checks that the host interfaces' local addresses didn't change, and if
they did, clears the cache.
The atomic.Pointer seems not needed anymore, tests with -race work
alright without warnings.
Have one server sitting at 110, with two distinct IP addresses,
each suitable for one BR in 110.
Server listens in 0.0.0.0
Clients in 111 and 112 check the reply address corresponds to that
of the particular interface of the particular BR used to talk to the
server.
@juagargi juagargi requested a review from a team as a code owner May 22, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant