Manage multiple NEAppProxyFlow(s)

Hello! I am using a NETransparentProxy and I need to manage multiple NEAppProxyFlow.

I am dealing with hundreds / thousands connections, so the one-thread-per-connection approach is really not feasible.

Regarding raw bsd sockets, I know multiple ways of achieving good results when managing a large number of sockets using:

  • poll()
  • kqueue
  • SwiftNIO library

but I am struggling to find a way to do something similar with flows.

My current "solution" is to create a new Task.detached for each new connection and have this Task 'block' on readData / readDatagrams. It works for low numbers of connections but it does not scale well when the number of connections increases.

Is there a way to achieve a similar result as poll() for sockets for flows? Otherwise, is there a way to make my current solution work? (even though I don't think it is able to scale well) I can provide more details about the architecture if needed, or code snippets.

Thank you!!

This is almost exactly the same question as on this thread. Or you and Horse Whisperer working on the same project? If not, that’s quite a coincidence.

Share and Enjoy

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

@eskimo Thank you for your answer! Yeah, it's the same project and I see you've already answered there :)

I do have another question though:
The only reason we're using a transparent proxy is to force certain apps traffic out of the physical interface.
Is there a chance that we could use networkInterface to set the flow interface to the physical one?
Not having to explicitly proxy those flows would be way preferable.

If yes, how and where would you set this property?
At the moment our code looks roughly like this:

class MyProxyProvider : NETransparentProxyProvider {
    override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        if appsThatIWantToManage.contains(flow.metaData.sourceAppSigningIdentifier)
            flow.open(withLocalEndpoint: nil) { error in
                startRecursiveReadAndWrite(flow)
            }
            return true
        }
        return false
    }

    func startRecursiveReadAndWrite(_ flow: NEAppProxyFlow) {
        // here we do the actual proxying of the traffic
    }

I can provide more details, if this is not enough.

Thank you again!

The only reason we're using a transparent proxy is to force certain apps traffic out of the physical interface.

I presume that these are apps that you don’t control?

Share and Enjoy

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

@eskimo Yes, exactly.
Otherwise it would have been easy to just use socket bind() against the physical interface address to achieve the same result

Manage multiple NEAppProxyFlow(s)
 
 
Q