On iOS, reconfiguring the VPN adapter kills any in progress NSURL task.

Hi,

We are running a VPN in a network extension using the NEPacketTunnelProvider APIs. When we re-configure the VPN adapter, such as on a roam, any active NSURL session task fails.

Using a test app we've been seeing that TCP streaming using BSD sockets remains unaffected, but NSURL tasks get killed. This is what the NSURL task fails with.

Code Block
021-04-05 17:18:31.202678-0400 TCPStreamer[17967:460702] Task <2B2AE33C-145B-46E5-96F0-15C1E21791B5>.<4006> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={​​​​​​​​_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x1144d42a0 {​​​​​​​​Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={​​​​​​​​_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}​​​​​​​​}​​​​​​​​, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <2B2AE33C-145B-46E5-96F0-15C1E21791B5>.<4006>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
  "LocalDownloadTask <2B2AE33C-145B-46E5-96F0-15C1E21791B5>.<4006>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=URL, NSErrorFailingURLKey=URL, _kCFStreamErrorDomainKey=1}​​​​​​​​
2021-04-05 17:18:31.202699-0400 TCPStreamer[17967:460702] The Internet connection appears to be offline.
2021-04-05 17:18:31.203880-0400 TCPStreamer[17967:460700] Connection 4007: received failure notification
2021-04-05 17:18:31.203897-0400 TCPStreamer[17967:460700] Connection 4007: failed to connect 1:50, reason -1
2021-04-05 17:18:31.203934-0400 TCPStreamer[17967:460700] Connection 4007: encountered error(1:50)
2021-04-05 17:18:31.204591-0400 TCPStreamer[17967:460700] Task <C0AB5E94-0CD8-4FBD-804F-99CA7C75F4CC>.<4007> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])


I am wondering if this is intended, or if this is something I should file a bug for. Running the same test on macOS does not produce this issue.

Thanks

I am wondering if this is intended, or if this is something I should file a bug for.

If you are re-establishing your network interface (in this case the virtual interface) then network failures can occur until the interface is back up. So in the case of a NSURLSessionDataTask, this does not seem unusual. As far a BSD socket is concerned, you may be experiencing the same thing here and not know it until data is written to the connection, or if you do not experience an issue with the socket, check that the socket was not going through the virtual interface to begin with.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
I wanted to follow up here and also ask, what does "such as on a roam," mean in this context? The reason I ask is because if the IP address changes to the interface then this will absolutely kill any TCP connections configured over it. In the case that the data is written to the socket and the interface has not changed, the data will most likely continue to buffer until the connection either is closed or is recovers and sends the data in the write buffer.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
@Matt Eaton

We are not changing the IP address on the interface.

I've attatched a more detailed log of what we are seeing.



When running the same tests on an iOS 12 device and any macOS device, we do not see the problem described. We see it on iOS 13 and iOS 14.

So I'm wondering if this behavior change is intended, or if there is a bug that should be filed.
It looks like you are running into a lost connection during this time. For example:

Code Block
default 18:45:48.380188-0400 TCPStreamerIOS [C1.1 172.31.92.11:80 failed channel-flow (satisfied (Path is satisfied), interface: utun2, ipv4, dns)] event: flow:disconnect @1.810s
default 18:45:48.380300-0400 TCPStreamerIOS nw_connection_report_state_with_handler_on_nw_queue [C1] reporting state failed error Network is down
default 18:45:48.380816-0400 TCPStreamerIOS Connection 1: received viability advisory(N)
error 18:45:48.393301-0400 TCPStreamerIOS Task <92517DA9-C5F3-4902-BFBF-97276888C780>.<1> finished with error [-1005] Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=53, NSURLSessionDownloadTaskResumeData={length = 4798, bytes = 0x62706c69 73743030 d4010203 04050607 ... 00000000 00001216 }, NSUnderlyingError=0x2829443c0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x280438050 [0x1e5744660]>{length = 16, capacity = 16, bytes = 0x10020050ac1f5c0b0000000000000000}, _kCFStreamErrorCodeKey=53, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <92517DA9-C5F3-4902-BFBF-97276888C780>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDownloadTask <92517DA9-C5F3-4902-BFBF-97276888C780>.<1>"


There is a tech note on this issue, Q&A QA1941, and how to handle these types of situations based on what type of request this is, but one approach here to take would be to retry the request after a second or two.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
We are running a Packet Tunnel Provider. The users of our application are running NSURL requests in their applications. We have no control over those NSURL requests. We are seeing their previously working applications breaking due to this.
On iOS, reconfiguring the VPN adapter kills any in progress NSURL task.
 
 
Q