Different behaviour of NWUDPSession's setReadHandler on different iOS version when remote server is unavailable

Recently found some different results on iOS 12 and iOS 14. I have an application that is using NEPacketTunnelProvider to establish a VPN tunnel with a remote server. The VPN tunnel is using NWUDPSession and process the inbound packets with the

open func setReadHandler(_ handler: @escaping ([Data]?, Error?) -> Void, maxDatagrams: Int)

If I shutdown the remote VPN server, on iOS 12 an error will occur

Error Domain=NSPOSIXErrorDomain Code=89 "Operation canceled"

but on iOS 14+ there will be no such error happens.

Is this a bug in iOS 14+ compared with iOS 12?

Is this a bug in iOS 14+ compared with iOS 12?

Not sure. If you feel like this is inconsistent behavior then you are welcome to open a bug report. However, there are a few things to looks at:

  1. What NWUDPSessionState tell you about the state of the session before the read is performed?

  2. On iOS 14 are you just seeing more packet's left to read queue and it takes longer for the session to be cancelled?

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

the isViable is still true, and state is still ready, and the packetFlow keeps reading packets and also can writeMultipleDatagrams without any problems. But once if I restart the VPN server's service, then it will give out errors like Error Domain=NSPOSIXErrorDomain Code=61 "Connection refused" Error Domain=NSPOSIXErrorDomain Code=89 "Operation canceled"

The really odd part here is that traffic still seems to be flowing without incident out ofwriteMultipleDatagrams. I can see traffic being read from readPackets on the virtual interface, but if your server is completely offline then it should not be receiving the datagrams from writeMultipleDatagrams. If it is receiving them then you know your server is not down yet. If it is not receiving them then you may want to take a look at the TCP side of things in your tunnel. On your TCP traffic, do you see the same thing? The reason I ask is because TCP will account for lost or retransmitted packets due to a peer to be unreachable, but UDP will not account for these kind of breakdowns because it will send traffic as without an acknowledgement back from the server.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

I don't know how it works but it seems this mechanism is not provided on iOS 14.

Did you try this with NWTCPConnection at all?

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

I tried with the VPN tunnel using a TCP connection with NWTCPConnection on iOS 14.7.1. If I stop the server then on the client side an error Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" will instantly show up in the console log.

Okay, this sounds like the answer. Since a UDP connection, or in this case, a NWUDPSession is not keeping track of the acknowledgments from the server, then issues like this take awhile to bubble to the surface. Unlike TCP which automatically expects an acknowledgement for every packet sent and is able to keep track of these types of server side issues.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Different behaviour of NWUDPSession's setReadHandler on different iOS version when remote server is unavailable
 
 
Q