Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix udp socket #449

Merged
merged 5 commits into from
Jul 19, 2021
Merged

Fix udp socket #449

merged 5 commits into from
Jul 19, 2021

Conversation

hannesm
Copy link
Member

@hannesm hannesm commented Jul 14, 2021

No description provided.

@hannesm
Copy link
Member Author

hannesm commented Jul 14, 2021

@mirage/core this came out of using stack-socket. unfortunately SO_REUSEPORT is only available from OCaml 4.12 onwards -- anyone has problems with dropping earlier OCaml releases in tcpip?

@djs55
Copy link
Member

djs55 commented Jul 14, 2021

I'm personally happy with OCaml 4.12+

@@ -30,6 +30,7 @@ let get_udpv4_listening_fd {listen_fds;interface} port =
Lwt.return @@ Hashtbl.find listen_fds (interface,port)
with Not_found ->
let fd = Lwt_unix.(socket PF_INET SOCK_DGRAM 0) in
Lwt_unix.(setsockopt fd SO_REUSEPORT true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this to allow multiple Unix processes to bind to the same port, to share responsibility for handling the requests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow the same unix process to bind to the same port several times, i.e. in DNS:

  • we listen on UDP Ipaddr_any, 53 (via a listen callback) -- calls bind(socket, *, 53)
  • once a UDP frame is received, we take some time to find an answer, and eventually send from (, 53) to the client -- this means we have to bind as well to (, 53).

Without the REUSEPORT we can't bind to the same ip, port pair multiple times.

@dinosaure
Copy link
Member

@mirage/core this came out of using stack-socket. unfortunately SO_REUSEPORT is only available from OCaml 4.12 onwards -- anyone has problems with dropping earlier OCaml releases in tcpip?

Hmmhmm, 4.12 is a bit higher for me. We should keep, at least, the support of the 3 last version of OCaml I think.

@hannesm
Copy link
Member Author

hannesm commented Jul 15, 2021

4.12 is a bit higher for me

ok, so what are the options?

  • leave tcpip broken as is (the socket stack can't receive on the same port as it wants to send on (via UDP))
  • provide a C stub that allows to set SO_REUSEPORT
  • rework this PR to be able to avoid SO_REUSEPORT (the commit 76b8578#diff-2df233b86746a505609a07125cd5a1ca625c8ddc4faba8ab44ddf9c2a0d9b578 introduced the create_socket asymmetry in Udpv4v6_socket) -- the create_socket exists since when sending UDP data we only want to use a single socket (and the get_udpv4v6_listening_fds may return two sockets (one for IPv4, one for IPv6 if both a static v4 and a v6 address is configured). Likely this can be refactored.

Eventually, the SO_REUSEPORT is not required if -- similar to how Udpv4_socket (and Udpv6_socket) re-use listening sockets for writing. Unfortunately the lifetime of a socket is then infinite (with file descriptors leakage) -- I guess the stack-socket needs some enhancements anyways (and there should be the counterpart to llsten_tcp/listen_udp that stops the listener).

3 last version of OCaml

From my memory, we agreed to the last 2 OCaml versions -- and 4.13 is upcoming soon. I also think that the tcpip stack is barely used by others than MirageOS unikernels. IMHO it is fine to bump to 4.12 (and 4.13) for this library.

So:

  • this PR here fixes some issues if you want to use UDP and the socket stack, esp. with dual stacks
  • there's more work needed to avoid leaking file descriptors
  • it is not urgent since it seems like nobody really uses the socket stack and UDP (we use it for developing and testing a dns resolver, but we can work with a local pin for now)

@hannesm
Copy link
Member Author

hannesm commented Jul 15, 2021

I opened #450 describing the leaks and potential fix(es). related to #446. I also moved the less controversial part to #451.

@dinosaure
Copy link
Member

From my memory, we agreed to the last 2 OCaml versions -- and 4.13 is upcoming soon. I also think that the tcpip stack is barely used by others than MirageOS unikernels. IMHO it is fine to bump to 4.12 (and 4.13) for this library.

So if we agree with that, it should fine so 👍. I largely prefer a non-buggy stack.

@avsm
Copy link
Member

avsm commented Jul 16, 2021 via email

This avoids file descriptor leaks and should fix #446 and #450
@hannesm
Copy link
Member Author

hannesm commented Jul 19, 2021

we revised the implementation to address the file descriptor leaks (should solve #446 / #450).
also, we removed the REUSEPORT socket option (which was really only needed in udpv4v6_socket), and instead reuse the same socket for listen and write (by adding some more logic into get_udpv4v6_listening_fd).

TL;DR: the socket stack disconnect function now cleanup file descriptors. the tests suite should pass nicely, and we don't require OCaml 4.12. we tested this with the dns-resolver unikernel which reads from and writes to a lot of udp sockets.

any reviews welcome, I'm keen to merge and release a new version of tcpip without the leaks (plus the other changes in previous PRs).

@hannesm hannesm merged commit edb9e7d into master Jul 19, 2021
@hannesm hannesm deleted the fix-udp-socket branch July 19, 2021 20:37
hannesm added a commit to hannesm/opam-repository that referenced this pull request Jul 19, 2021
CHANGES:

* This allows to listen on the same port as sending via UDP in the dual socket
  stack, and avoids file descriptor leaks in the socket stack.
* Socket stack: avoid file descriptor leaks (remember opened file descriptors in
  data structure, close them in disconnect)
  (mirage/mirage-tcpip#449 @reynir @hannesm, fixes mirage/mirage-tcpip#446 mirage/mirage-tcpip#450)
* Socket stack: convert an incoming packet on a dual socket to v4 source IP if
  received via IPv4 (mirage/mirage-tcpip#451 @reynir @hannesm)
* Allow freestanding compilation without opam (mirage/mirage-tcpip#447 @sternenseemann)
* Adapt to alcotest 1.4.0 breaking change (mirage/mirage-tcpip#448 @craigfe)
hannesm added a commit to hannesm/opam-repository that referenced this pull request Jul 19, 2021
CHANGES:

* This allows to listen on the same port as sending via UDP in the dual socket
  stack, and avoids file descriptor leaks in the socket stack.
* Socket stack: avoid file descriptor leaks (remember opened file descriptors in
  data structure, close them in disconnect)
  (mirage/mirage-tcpip#449 @reynir @hannesm, fixes mirage/mirage-tcpip#446 mirage/mirage-tcpip#450)
* Socket stack: convert an incoming packet on a dual socket to v4 source IP if
  received via IPv4 (mirage/mirage-tcpip#451 @reynir @hannesm)
* Allow freestanding compilation without opam (mirage/mirage-tcpip#447 @sternenseemann)
* Adapt to alcotest 1.4.0 breaking change (mirage/mirage-tcpip#448 @craigfe)
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.

5 participants