Question about listen on the utun interface to serve as system dns server

Hello,

Recently I am trying to add stub dns server to my Network Extension (a VPN app), after some research on this forum, and since my language is C, I have the following plan:

  1. create a udp socket which use setsockopt(IP_BOUND_IF) to bound the socket to the utun if index obtained, and also bind to the address of the utun address I set(let's say 192.168.99.2), then listen on the udp port 53 which is ready to handle dns request.
  2. configure the dns server to 192.168.99.2 in the provider's Network Settings, thus iOS system will send udp query to the udp socket created in step 1, and it can then do some split dns function such as resolve using local interface (cellular or wifi), or some nameserve which will be routed to the VPN tunnel (will create new UDP socket and do IP_BOUND_IF to ensure the traffic will enter the VPN tunnel), and the result should be return to the system and then the non VPP apps.

But I observer weird issue, indeed I can get the system send the dns request to the listening udp socket and I can get the result write to the system(address like 192.168.99.2:56144, the port should be allocated by the iOS system's DNS component) without any failure(I did get some error before due to I using the wrong utun if index, but fixed it later), but it seems non VPN app like browser can't get the resolved ip for domains.

I want to ask is this limited by the sandbox? or any special sock opt I need to do.

Thanks.

PS:

  1. in the provider's network settings, all the system's traffic will be point to the utun, which means the VPN process will process all the traffic.
  2. the reason I do not set the dns server to utun peers side which is my userspace networking stack's ip (192.168.99.1) is the stack is not be able to leverage some dns libraries due to architecture issue. (it's fd.io vpp which we ported to apple platform).
Answered by DTS Engineer in 834413022

I just wanna make it clear that UTUN interfaces are an implementation detail of the packet tunnel provider mechanism on iOS, and on macOS for that matter. While it’s fine to assume that your packet tunnel provider has a network interface, you shouldn’t assume that it’s a UTUN interface, or assume any other interface-specific details about it.

The canonical way to get your tunnel’s interface is via the virtualInterface property. That returns an NWInterface, and you can get BSD-specific stuff, like the BSD interface name, from its properties [1].

Share and Enjoy

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

[1] If you’re using a C-based language this is an nw_interface_t and the routines are nw_interface_get_name / nw_interface_get_index.

Accepted Answer

sorry for the post, it seems in some case my code can't correct find the utun ifindex, this plan actually works.

I’m glad to hear that you got this working, but any time I see NE products mess around with UTUN interfaces I start to worry )-: So, I have a couple of follow-up questions:

  • What platform is this?

  • What sort of NE provider is this? A packet tunnel provider?

  • If this is the Mac, are you using sysex or appex packaging?

Share and Enjoy

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

Yes, mess around with UTUN interface is hard but joyful:)

Here is the answers for the question, the platform is iOS, it's a packet tunnel provider.

Will also consider to release a macOS app later if we have more time.

I just wanna make it clear that UTUN interfaces are an implementation detail of the packet tunnel provider mechanism on iOS, and on macOS for that matter. While it’s fine to assume that your packet tunnel provider has a network interface, you shouldn’t assume that it’s a UTUN interface, or assume any other interface-specific details about it.

The canonical way to get your tunnel’s interface is via the virtualInterface property. That returns an NWInterface, and you can get BSD-specific stuff, like the BSD interface name, from its properties [1].

Share and Enjoy

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

[1] If you’re using a C-based language this is an nw_interface_t and the routines are nw_interface_get_name / nw_interface_get_index.

Thanks a lot for the msg, I was noticed virtualInterface property, but sadyly found it's only avalable on iOS 18+, may be in the future we'd better migrate to use this for reliably get the bsd interface and index.

it's only avalable on iOS 18+

Indeed. However, that’s actually a great story:

  • On iOS 18 and up use that property. That isolates you from potential changes in the future.

  • On older systems, implement whatever hacky alternative [1] you want because those systems aren’t going to change [1].

Share and Enjoy

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

[1] Well, within reason. We do ship security updates for older systems and if you do something extremely silly then it’s possible that you might be broken by such an update. My point is that you have a lot more scope for creative workarounds if you don’t have to worry about long-term binary compatibility.

Hi,

Thanks for the suggestion, I think that's resonable and I modify my app code to follow the guideline.

Actually I do find some issue because my quirks sometimes find a stale ifindex due to previous utun interface seems does not cleanup, in iOS 18 its quite easy to get the correct index, below iOS 18 I have to fix the logic to make it's more stable...

Question about listen on the utun interface to serve as system dns server
 
 
Q