3

I need to detect in WinRT application on which protocols there is an internet access (IPv4/IPv6/both). I've got the following code to determine supported protocols:

enum IpVersion
{
    None = 0,
    IPv4 = 1,
    IPv6 = 2,
    IPv46 = 3
}

IpVersion GetIpVersion(ConnectionProfile profile)
{
    var result = IpVersion.None;

    if (profile != null && profile.NetworkAdapter != null)
    {
        var hostnames = NetworkInformation.GetHostNames().Where(h => h.IPInformation != null && 
                                                                h.IPInformation.NetworkAdapter != null &&
                                                                h.IPInformation.NetworkAdapter.NetworkAdapterId == profile.NetworkAdapter.NetworkAdapterId);
        foreach (var hostname in hostnames)
        {
            if (hostname.Type == HostNameType.Ipv4)
            {
                result |= IpVersion.IPv4;
            }
            else if (hostname.Type == HostNameType.Ipv6)
            {
                result |= IpVersion.IPv6;
            }
        }
    }

    return result;
}  

I use it like this:

GetIpVersion(NetworkInformation.GetInternetConnectionProfile()); 

Now I would like to know if there is an internet access on each available protocol. Of course I could ping something, but I wonder if there is some SDK method for it. Those information are available in Wi-Fi status window:

WiFi-status

There is a method, which can return for example NetworkConnectivityLevel.InternetAccess, but it doesn't contain information on which protocol there is a connection.

bool internetAccess = connectionProfile.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess
Wojciech Kulik
  • 7,823
  • 6
  • 41
  • 67
  • Sorry, is this something though? http://stackoverflow.com/questions/10336521/query-local-ip-address – kevintjuh93 Sep 25 '15 at 12:17
  • @KevinKal I based on this, however I don't detect ip address, but IP version. Read the question carefully. – Wojciech Kulik Sep 25 '15 at 12:19
  • I am reading it carefully. I can't find anything either about getting the type from existing methods. Why not try to determine the IP version through the rawName... must be possible I think! – kevintjuh93 Sep 25 '15 at 12:21
  • @KevinKal if you are connected to the network, it can support connection via IPv4 or IPv6 or both. I need to know If my network supports BOTH, howevere hostname from my code contains enum HostNameType which doesn't have value "both". – Wojciech Kulik Sep 25 '15 at 12:26
  • 1
    @KevinKal nope, it is possible that you won't have internet connection via IPv4. Maybe I should take a look on those ConnectionProfiles. Unfortunately I will have access to IPv6 on Monday, so then I will check it out. – Wojciech Kulik Sep 25 '15 at 12:30

2 Answers2

1

In your code, what is the ConnectionProfile type? It's difficult to understand the question without a good, minimal, complete code example and a precise explanation of what that code does and why that's different from what you want.

That said, if I understand the question correctly, you are trying to determine if your connection to the Internet supports both IPv4 and IPv6. If so, then I don't see any way you could do this from the API level. The local PC could in fact have the IPv4 protocol installed without being attached to a network that would allow traffic on that protocol to be transmitted. Even if the LAN supported that protocol, there could be something about the Internet connection itself that supported only IPv6.

Same thing applies the other way (i.e. having local IPv6 support, but only IPv4 over the Internet).

It seems to me that the only reliable approach is the same as is needed in many other situations: just try it and see if it works. I.e. attempt to connect to your remote endpoint on the Internet via the desired protocol version; if it fails, then it's not supported. If it succeeds, it is supported.


EDIT:

Thanks for the update to the question. It's still not really the best code example, but it refines the question a little.

I'm still not 100% what you need to do, nor whether I have the most useful approach for you. But here is a short program that I think does what you want:

XAML:

<Page x:Class="TestSO32781692NetworkProtocolConnectivity.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:TestSO32781692NetworkProtocolConnectivity"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
      mc:Ignorable="d">

  <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel.Resources>
      <Style TargetType="TextBlock">
        <Setter Property="FontSize" Value="24"/>
      </Style>
    </StackPanel.Resources>
    <StackPanel Orientation="Horizontal" Margin="10, 50, 10, 0">
      <TextBlock Text="IpV4: "/>
      <TextBlock Text="{Binding IpV4}"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="10, 10, 10, 0">
      <TextBlock Text="IpV6: "/>
      <TextBlock Text="{Binding IpV6}"/>
    </StackPanel>
    <Button Content="Check Network" Click="Button_Click"/>
    <ListBox ItemsSource="{Binding Profiles}"/>
  </StackPanel>
</Page>

C#:

using System;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Networking;
using Windows.Networking.Connectivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace TestSO32781692NetworkProtocolConnectivity
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public static readonly DependencyProperty IpV4Property = DependencyProperty.Register(
            "IpV4", typeof(bool), typeof(MainPage), new PropertyMetadata(false));
        public static readonly DependencyProperty IpV6Property = DependencyProperty.Register(
            "IpV6", typeof(bool), typeof(MainPage), new PropertyMetadata(false));
        public static readonly DependencyProperty ProfilesProperty = DependencyProperty.Register(
            "Profiles", typeof(ObservableCollection<string>), typeof(MainPage), new PropertyMetadata(new ObservableCollection<string>()));

        public bool IpV4
        {
            get { return (bool)GetValue(IpV4Property); }
            set { SetValue(IpV4Property, value); }
        }

        public bool IpV6
        {
            get { return (bool)GetValue(IpV6Property); }
            set { SetValue(IpV6Property, value); }
        }

        public ObservableCollection<string> Profiles
        {
            get { return (ObservableCollection<string>)GetValue(ProfilesProperty); }
            set { SetValue(ProfilesProperty, value); }
        }

        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            bool ipV4 = false, ipV6 = false;
            ConnectionProfile internetProfile = NetworkInformation.GetInternetConnectionProfile();

            Profiles.Clear();
            Profiles.Add("Internet profile: " + internetProfile.ProfileName);

            var hostNames = NetworkInformation.GetHostNames()
                .Where(h => h.IPInformation != null &&
                       h.IPInformation.NetworkAdapter != null);

            foreach (HostName hostName in hostNames)
            {
                ConnectionProfile hostConnectedProfile =
                    await hostName.IPInformation.NetworkAdapter.GetConnectedProfileAsync();

                if (hostConnectedProfile.NetworkAdapter.NetworkAdapterId == internetProfile.NetworkAdapter.NetworkAdapterId)
                {
                    Profiles.Add("Host adapter: " + hostName.DisplayName);
                    if (hostName.Type == HostNameType.Ipv4)
                    {
                        ipV4 = true;
                    }
                    else if (hostName.Type == HostNameType.Ipv6)
                    {
                        ipV6 = true;
                    }
                }
            }

            IpV4 = ipV4;
            IpV6 = ipV6;
        }
    }
}

Note that this code example does not use the NetworkInformation.GetConnectionProfiles() method. Though that seemed like a promising way to extend your current approach, and though the documentation actually promises that "Calling the GetConnectionProfiles method retrieves profiles for all connections currently established on the device, including the Internet connection." [emphasis mine], this turns out to not be the case. In particular, at least on my machine where Hyper-V is installed and enabled (a common scenario for those doing WinRT/Windows Phone development :) ), the ConnectionProfile object returned by NetworkInformation.GetInternetConnectionProfile() is in fact not included in the collection of profiles returned by NetworkInformation.GetConnectionProfiles().

So instead, what the code example above does is to simply identify any host name objects that appear to correspond with the ConnectionProfile returned by GetInternetConnectionProfile().

Unfortunately, I don't actually have IPv6 support where I am, so I was unable to test this fully. I was able to test the above on a different network where IPv6 to the Internet is supported, and I can now confirm that, at least in my test, it works as expected. The code correctly detects Internet connectivity on both the IPv4 and IPv6 protocols. I'm hopeful that the above does what you need.


By the way, besides the obvious problem that (as you noted) using GetNetworkConnectivityLevel() does not provide the actual protocol in use, I found that that method does not even return information that at least intuitively would be considered correct.

In particular, calling FindConnectionProfilesAsync(new ConnectionProfileFilter { IsConnected = true }) does return the profile corresponding to the connection (e.g. wireless network) I'm using to connect to the Internet, but when I call GetNetworkConnectivityLevel() on that profile, it returns LocalAccess only. I'm guessing this is related to the issue I noted above with having Hyper-V installed.

This can be worked around by comparing the ConnectionProfile returned by the GetConnectedProfileAsync() method for the NetworkAdapter of each connected profile returned by FindConnectionProfilesAsync() to the profile returned by GetInternetConnectionProfile(). Indirecting on the profile for the top-level profile's network adapter seems to yield the expected Internet connection profile.

Of course, working around that issue does not solve the question of the protocol in use. I mention it only in the event someone else is looking at this answer more for the profile-management aspects and not the protocol connectivity question.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Thank you for the effort with this answer, I will check it out this week and let you know. Did you try to turn off DNS for IPv6 (no internet connection then) and verify if it works properly? Because it looks like this code verifies only if there is assigned IP address for each protocol. So there is possible case, when we've got internet connection only on IPv4 - GetInternetConnectionProfile() will return this profile, but another hostname (IPv6) is not connected to the internet, so this method will return both as they belong to the same profile (with internet connection) – Wojciech Kulik Oct 05 '15 at 22:21
  • I did not do extensive testing with this code. I can tell you that at a site with only IPv4 service on the Internet side, but IPv6 on the LAN side, it correctly identifies the IPv4 hostname as being connected and the IPv6 hostname as not being connected. – Peter Duniho Oct 05 '15 at 23:06
  • Unfortunately this code doesn't work properly. For IPv4 only, if I mess up with addresses (so that I've got limited access or no network access), it still shows me that there is connection on IPv4. For IPv6 only, it shows me always that both protocols have internet connectivity (even if for none of them it's true). If it's still unclear what I want to achieve, look at screenshot in my post with "Wi-Fi Status" window. I want to get exactly the same information about IPv4 & IPv6 (I mean Internet = True, anything else = False). – Wojciech Kulik Oct 06 '15 at 12:42
1

Understand the problem:

What actually do we need to know about the internet connection to assume that there is a connectivity through the specific protocol?

There are two things that we need to check:

  1. If we are able to connect using IP addresses.
  2. If we are able to resolve domain names to IP addresses using DNS servers.

Solution:

We can use domain name resolving to check which protocols are supported.

It works, because if we are able to connect to DNS server, we are able to connect through that protocol using IP addresses (I mean in general, assuming that there are no firewall rules, network issues etc.). Then if we get in response IPv6 address of some domain, it means the DNS server is set and working for that protocol.

Code sample:

// the order is important, if we want to support bitwise OR: IPv4 | IPv6 equals IPv4and6
public enum IpVersion
{
    None,
    IPv4,
    IPv6,
    IPv4and6
}

public async Task<IpVersion> GetCurrentIpVersion()
{
    try
    {
        // resolves domain name to IP addresses (may contain several)
        var endPointPairs = await DatagramSocket.GetEndpointPairsAsync(new HostName("google.com"), "0");
        if (endPointPairs == null)
        {
            return IpVersion.None;
        }

        // detect which IP version is supported
        var result = IpVersion.None;
        foreach (var endPoint in endPointPairs)
        {
            if (endPoint.RemoteHostName != null)
            {
                if (endPoint.RemoteHostName.Type == HostNameType.Ipv4)
                {
                    result |= IpVersion.IPv4;
                }
                else if (endPoint.RemoteHostName.Type == HostNameType.Ipv6)
                {
                    result |= IpVersion.IPv6;
                }
            }
        }
        return result;
    }
    catch
    {
        return IpVersion.None;
    }
}

Tests:
I tested it on IPv4-only, IPv6-only and IPv4 + IPv6 networks - works as expected.

Sample project:
IpVersionDetection - GitHub

I described this solution also here:
WinRT - how to detect supported IP version

Wojciech Kulik
  • 7,823
  • 6
  • 41
  • 67