How to multiplex possibly thousands of NEAppProxyFlows ?

Hi,

I am writing a transparent proxy (using NETransparentProxyProvider) which could potentially multiplex thousands of flows.

When i've done this in the past on other platforms i've used libev or epoll - but NEAppProxyFlow (such as NEAppProxyTcpFlow) don't work with any of those approaches afaict, it doesn't even appear to work with swift-nio - what is the recommended way to multiplex thousands of flows?

I still intend to use swift-nio when i manage the real sockets (which proxy the flows), but how do i multiplex the NEAppProxyFlows themselves? Can someone suggest a highly scalable design? I'm new to this, and haven't found a good solution yet.

Thanks

Replies

what is the recommended way to multiplex thousands of flows?

There aren’t any alternative paths each. You have to use the read and write methods on each flow you get. Fortunately these aren’t blocking, so you’re not burning a thread per flow. Still, there isn’t a way to get at the underlying socket to use with traditional Unix-y primitives like select [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] And given our pivot towards the user space networking stack, there’s no guarantee that there even is an underlying socket.

@eskimo Thanks!

The docs aren't clear on this (at least to me) - but you're saying i can just call flow.readData { data, error in ... } once after the flow is opened - and it'll automatically invoke that block each time the flow has data? Or you're saying that i need to call flow.readData { data, error in ... } in a loop but that it just won't block (so i could potentially iterate over an array of flows calling readData() on each one in a loop without worrying about any of them blocking) ?

but you're saying i can just call

Not really either (-: The typical approach is to make a read call and then, in the completion handled, if all is still good, make another read call. So, something like this:

func startNextRead() {
    flow.read { … args … in
        … process the args …
        if … you want to continue reading … {
            startNextRead()
        }
    }
}

You do this for each flow independently, so there’s no loop per se. Rather, once you’ve successfully opened the flow you call startNextRead() initially to kick off this process.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"