13 Replies
      Latest reply on Dec 10, 2018 7:36 AM by dinesh192
      mjc Level 1 Level 1 (0 points)

        Does CFNetwork provide API to open a socket on a particular interface? This post suggests

        using SCNetworkInterfaceCopyAll and SCNetworkInterfaceGetInterfaceType to find an interface with a desired type. But I get an error saying SCNetworkInterfaceCopyAll is unavailable in Xcode 8.3.2.

         

        If SCNetworkInterfaceCopyAll is unavailable, how would you search the result of getifaddrs to find an interface with a type (e.g. kSCNetworkInterfaceTypeIEEE80211 (wifi) vs kSCNetworkInterfaceTypeWWAN (cell)).

         

        Thanks!

        • Re: API to open a socket on a specific interface
          eskimo Apple Staff Apple Staff (12,465 points)

          Does CFNetwork provide API to open a socket on a particular interface?

          What platform are you working on?

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: API to open a socket on a specific interface
              mjc Level 1 Level 1 (0 points)

              Hi Quinn,

               

              I'm working on iOS.

               

              Thanks for your response!

                • Re: API to open a socket on a specific interface
                  eskimo Apple Staff Apple Staff (12,465 points)

                  I'm working on iOS.

                  OK then, the System Configuration framework APIs you’re referencing are only available on macOS.

                  This issue breaks down as follows:

                  • Binding a connection to an interface

                  • Selecting an interface.

                  I’ll discuss each in turn.


                  If you’re working with low-level APIs — those that let you get at the socket (in the BSD Sockets sense) for the connection — you can force the connection to run over a specific interface in two ways:

                  • By using bind to bind the source address of the socket to the interface’s IP address

                  • Using the IP_BOUND_IF socket option

                  This works well for BSD Sockets and things tightly tied to it (like CFSocket and GCD).  It does not work at all for higher-level APIs, like NSURLSession.  You can bodgy it into working for CFSocketStream, but that has some serious caveats.


                  With regards identifying an interface, iOS doesn’t have any really good options on that front.  You can call getifaddrs to get an interface list and then, for AF_LINK interfaces, muddle around in struct if_data, but there are some serious drawbacks:

                  • The constants needed to test ifi_type are not part of the iOS SDK, and hence not officially supported

                  • Even when you identify the interface type, you’ll find that there are often multiple active interfaces of each type and there’s no supported way to pick between them


                  What’s your high-level goal here?  Specific subsystems within iOS have supported ways to run connections over a specific interface, so there might be a subsystem-specific mechanism that can help you out.  For example, captive network apps can force the connection to run over the captive Wi-Fi interface by calling -bindToHotspotHelperCommand:.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: API to open a socket on a specific interface
                      mjc Level 1 Level 1 (0 points)

                      Thanks for your response!

                       

                      Our goal is the ability to connect to an endpoint via wifi and/or cell based on internally calculated connection metrics, over a VPN using a custom protocol. The ideal, high-level API would be something like a property on an instance of NWTCPConnection that specified the type of interface used by that connection.

                       

                      Since that’s not available, is it possible for a custom NEPacketTunnelProvider to create a TCP connection through a VPN tunnel by using CFSocket or GCD, instead of by using a NWTCPConnection from createTCPConnectionThroughTunnel? That seems to be the only way something like this could work.

                       

                      If there were a way to ask the system return a NWTCPConnection when any other path were available, not just when a better path were available as determined by the system, then we wouldn’t need to use lower-level APIs.

                       

                      Thanks!

                        • Re: API to open a socket on a specific interface
                          eskimo Apple Staff Apple Staff (12,465 points)

                          Our goal is the ability to connect to an endpoint via wifi and/or cell based on internally calculated connection metrics, over a VPN using a custom protocol.

                          I’m confused by this.  It seems to me that the VPN tunnel would be running over one interface or the other, so if you connect through the tunnel you’ll inherent the interface choice from there.

                          Or are you planning to have two tunnels, one over WWAN and one over Wi-Fi, and then choose between them?

                          Share and Enjoy

                          Quinn “The Eskimo!”
                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                          let myEmail = "eskimo" + "1" + "@apple.com"

                            • Re: API to open a socket on a specific interface
                              mjc Level 1 Level 1 (0 points)

                              Yes, we want to have two tunnels, one over WWAN and one over Wi-Fi, and then choose between them.

                                • Re: API to open a socket on a specific interface
                                  eskimo Apple Staff Apple Staff (12,465 points)

                                  Yes, we want to have two tunnels, one over WWAN and one over Wi-Fi, and then choose between them.

                                  OK.  The bit that confused me earlier was this:

                                  … instead of by using a NWTCPConnection from createTCPConnectionThroughTunnel?

                                  The ‘through tunnel’ APIs create a connection through the tunnel.  As a packet tunnel provider, if you want to create a connection for the tunnel, you use -createTCPConnectionToEndpoint:xxx (not -createTCPConnectionThroughTunnelToEndpoint:xxx).

                                  Having said that, the use of NWTCPConnection for the packet tunnel is not mandatory.  It’s fine to use other APIs, including BSD Sockets, for this.

                                  The gotcha here isn’t getting the TCP connection to run over a specific interface, which you can do with BSD Sockets and other low-level APIs, it’s identifying the correct interface to use.

                                  Share and Enjoy

                                  Quinn “The Eskimo!”
                                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                  let myEmail = "eskimo" + "1" + "@apple.com"

                                    • Re: API to open a socket on a specific interface
                                      mjc Level 1 Level 1 (0 points)

                                      I see quite a few interfaces on the device I’m using: en0—en2, pdp_ip0—pdp_ip4, ap1, awdl0, ipsec0—ipsec4, utun0, lo0.

                                       

                                      A common approach people take to identifying interfaces on iOS is to assume that the interface named en0 is the preferred wifi interface, and the interface named pdp_ip0 is the preferred cellular interface. Do you have any advice about that approach and/or how to more reliably identify the correct interface to use?

                                       

                                      Thanks!

                                        • Re: API to open a socket on a specific interface
                                          eskimo Apple Staff Apple Staff (12,465 points)

                                          A common approach people take to identifying interfaces on iOS is to assume that the interface named en0 is the preferred wifi interface, and the interface named pdp_ip0 is the preferred cellular interface.

                                          Hardwiring interface names is a really bad idea.

                                          Do you have any advice about that approach and/or how to more reliably identify the correct interface to use?

                                          No.  I’m going to quote myself here, namely, my 28 Apr 2017 post on this thread:

                                          With regards identifying an interface, iOS doesn’t have any really good options on that front.

                                          As to what the best option is, that’s somewhat context sensitive, as I’ve discussed above.

                                          Share and Enjoy

                                          Quinn “The Eskimo!”
                                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                          let myEmail = "eskimo" + "1" + "@apple.com"

                                            • Re: API to open a socket on a specific interface
                                              NotMyName Level 4 Level 4 (875 points)

                                              To follow this, if it hasn't been said recently enough, if the undocumented nature of the interfaces (naming or otherwise) is a problem, please file feature requests so the Apple staff have numbers to justify the work.

                                               

                                              After all, how the heck are you supposed to know what "awdl" or "utun" is, let alone what "en" is?  That sort of stuff is supposed to be documented by the POSIX distrubtion.

                                              • Re: API to open a socket on a specific interface
                                                lbcy Level 1 Level 1 (0 points)

                                                I too am trying to concurrently direct traffic out through either wifi or cellular (based on some policies/business logic similar to the OP) but running into problems.

                                                 

                                                The approaches described here (and similar others here and here) to bind a BSD socket to cellular - via bind(ip of cellular) or setsockopt(IP_BOUND_IF, cellular i/f) - work great when performed within an app but do not work when performed from within my Packet Tunnel provider. The binding itself succeeds, but then any attempts to connect on the cellular-bound socket always fail with EADDRNOTAVAIL.

                                                 

                                                I ran some additional experiments and found:

                                                1. The connect works perfectly fine when done from an app (instead of within my PT provider). I also tried this as a separate app while my PT provider was actively running at the same time and had no problems.
                                                2. I can connect() on the cellular-bound socket from within my packet tunnel provider as long as it is before I call the startTunnel completion handler (i.e. before VPN tunnel is brought up by the system). IOW, the only time the connect() fails is when I try to do it from within my PT provider and only after my tunnel is started (after I call the completion handler).
                                                3. The same EADDRNOTAVAIL occurs with both TCP and UDP sockets.
                                                4. The EADDRNOTAVAIL doesn't seem to be related to the port already being used (which is the typical cause), since I'm calling bind() with 0 for the port.

                                                 

                                                So, I'm wondering:

                                                1. Am I doing something wrong, i.e. is there something subtlely different that needs to be done within a PT provider that isn't typically required in an app?
                                                2. Is there a known issue with connecting cellular-bound sockets from within a tunnel provider?
                                                3. Is there some other method I can use to send some subset of my traffic over cellular, while still concurrently sending most of my traffic over wifi? I'm running a single full tunnel (not split) and finding that createTCPConnectionToEndpoint only allows me to send out over the current default network (not selectively and concurrently out either cellular or wifi when both are available).

                                                 

                                                Any help would be greatly appreciated. Thanks!

                                                  • Re: API to open a socket on a specific interface
                                                    eskimo Apple Staff Apple Staff (12,465 points)

                                                    … do not work when performed from within my Packet Tunnel provider.

                                                    That’s not a huge surprise.  Packet tunnel providers operate in a restricted networking environment that results in a bunch of oddities.  For example, we specifically go out of our way to ensure that traffic from the packet tunnel provider does not go through any other VPN interface [1].

                                                    Unfortunately I don’t have any easy answers for you here.  I still don’t have a solid handle on how these restrictions get applied, and thus how they might interact with your WWAN requirements.  My only suggestion is that you open a DTS tech support incident so that I can allocate the time to research this properly.

                                                    Share and Enjoy

                                                    Quinn “The Eskimo!”
                                                    Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                    let myEmail = "eskimo" + "1" + "@apple.com"

                                                    [1] Although, even more strangely, that restriction is not currently being enforced by macOS (r. 39974413).

                                                    • Re: API to open a socket on a specific interface
                                                      dinesh192 Level 1 Level 1 (0 points)

                                                      Have you got any solution for this.

                                                      I am also trying the same but no luck.