Some questions about flows in mirage

My first question would be how do you get around checking if a flow is closed, without reading and checking if it returns Eof.

I’m writing a client side load balancing library, where connections are persistent and pooled, and for the health check I would prefer not to have to read because I don’t want to lose data, and the only thing I could find where I could, is if I used an FD backed flow which would break compatibility.

The other option I have is creating a custom flow functor with type flow being {flow: F.flow; pending: cstruct.t list} .

While I have your attention, I read in the docs that the STACK api stops multiple listeners on the same port, is it possible to remove the default behavior of StackListeners, and enabling SO_REUSEPORT, to perform hole punching.


I don’t understand your reasoning behind “without reading”. How mirage-flow (see docs) is used, e.g. in mirage-tlstunnel is to have a dedicated reader task for each flow, which waits in read, and as soon as something is received, calls write to the other flow. Now, if an error happens during read or write, the flow is teared down. The mirage-flow itself does not have any pending buffer (I think mirage-channel has such functionality).

The implementation here (this uses the OCaml TCP/IP stack, the Unix Socket based one behaves in a similar way) replaces a potential existing listener on port YY with the new listener. I’m not sure what you mean with perform hole punching, but I think the implemented behaviour is what you requested!?

For question 1, I just realized, that I could just add a closed bool field, that I can set on Eof or calls to close.

As for question 2 hole punching is basically a way to traverse nats, or in my use case to get around AWS security groups, by first binding a socket on host A, then reusing that socket to connect to host B.

I’m honestly just super confused with how that would work with flows.

by “socket” you mean IP and port pair? Since create_connection does not allow to specify a source port (but instead selects one at random), this does not seem to be possible atm. Of course, mirage-protocols (and the implementation in mirage-tcpip) can be extended with a source port (which imho makes sense). But since there is no unlisten_tcpv4, there’s no way atm to drop the port & callback from the hashtable which is looked up in when a TCP segment is received, I suspect this needs to be extended as well.

Pretty much, I’m going to try using
let port = 9000 in
STACK.listen_udpv4 stack ~port cb;
STACK.listen stack >>= fun () ->
UDP.write ~src:port buf

To see if it works.

Note: vpnkit is doing something similar (bookkeeping incoming connections), but it hooks at the net level instead of using a full stack. It’s probably over-complicated for your needs, but maybe it could help :slight_smile:

your code now uses UDP - which I’d expect to work as you write it, but I doubt that replacing UDP with TCP will work with MirageOS (mirage-tcpip/mirage-flow) atm…

I’ve been debating whether I should implement UTP for mirage for a long time either way. Protocol RFC. LEDBAT.

Even if I never do with the way you guys implemented TCP, you’ve made it a lot easier to write reliable udp protocols by cherry picking the aspects of TCP one would need, or to tunnel it, and I love that, the major down side being the outside world won’t be able to understand it, if I were afraid to reinvent the wheel a bit I wouldn’t be using ocaml.