0

I'm trying to fetch the currently used network adapter. For example, if I'm receiving a web request in ASP.NET Core, I want to know which adapter is being used to handle that request.

Thanks!

NOP-MOV
  • 792
  • 2
  • 8
  • 28
  • I thought about "netstat" and linking to process then filtering by remote client ip (which I do have) but that would be slow due to parsing and execution, incompatible on Linux (would have to parse a different output). I tried launching a new UdpClient yet as Scott mentioned, it is not reliable because we don't really know which NIC will be selected by Windows TCP/IP kernel driver. I read about it and it seems that the NIC decision algorithm gives a clear priority for the same process on a previously used NIC so it will probably work most of the times, but we can't really count on it. – NOP-MOV Oct 06 '17 at 07:51
  • Also, I though about tricking the NIC balancing algorithm by creating a UdpClient with the destination IP the same as the remote user (which I can easily fetch by inspecting the HTTP request). This will mean a socket on the same process to the same remote IP. The thing is I don't know if UdpClient constructor really selects a NIC before invoking .connect() or is it just a dummy default NIC, which will probably be the case as it is really fast. Also, it can be out of C# control anyway, decided by the kernel. Weird thing MSFT doesn't have an interface exposing which NIC is being used. – NOP-MOV Oct 06 '17 at 07:53
  • Just finished reading some Core code, it seems that UdpClient is invoking Winsock's create socket upon construction and then "Bind()". As I understand, Windows TCP/IP driver is choosing the NIC upon creation of a socket which basically means that creating a UdpClient instance, pointed to the remote IP (which we know from the HTTP request) will, with very high probability, get assigned to the same NIC that was chosen for the incoming packet. Still requires some digging but seems good enough for most. – NOP-MOV Oct 06 '17 at 15:48
  • Would using the [`HttpContext.Connection` property](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext.connection) and [`ConnectionInfo.LocalIpAddress` property](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.connectioninfo.localipaddress) to get the local IP address and then matching that up with the results of [`NetworkInterface.GetAllNetworkInterfaces()`](https://learn.microsoft.com/dotnet/api/system.net.networkinformation.networkinterface.getallnetworkinterfaces) give you the information you need? – Lance U. Matthews Oct 07 '17 at 21:11
  • Thanks for the suggestion, will do that and will update. BTW, is HttpContext working for Azure Functions runtime now that it works with .NET Core? Thanks. – NOP-MOV Oct 08 '17 at 12:36

1 Answers1

2

You can use the HttpContext.Connection.LocalIpAddress property to get the IP address of the local (server) end of the connection, then find the NetworkInterface instance that has the same address bound to it.

using System.Linq;
using System.Net.NetworkInformation;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult GetInterfaceInfo()
        {
            var connectionLocalAddress = HttpContext.Connection.LocalIpAddress;
            var connectionLocalInterface = NetworkInterface.GetAllNetworkInterfaces()
                .Where(iface => iface.GetIPProperties().UnicastAddresses.Any(unicastInfo => unicastInfo.Address.Equals(connectionLocalAddress)))
                .SingleOrDefault();
            var results = new {
                NetworkInterface = connectionLocalInterface,
                IPInterfaceProperties = connectionLocalInterface?.GetIPProperties(),
                IPInterfaceStatistics = connectionLocalInterface?.GetIPStatistics(),
                IPv4InterfaceStatistics = connectionLocalInterface?.GetIPv4Statistics()
            };

            return Json(
                results,
                new JsonSerializerSettings() {
                    ContractResolver = new IgnorePropertyExceptionsResolver(),
                    Formatting = Formatting.Indented
                }
            );
        }
    }
}

Accessing http://localhost:12345/Home/GetInterfaceInfo then produces...

{
    "NetworkInterface": {
        "Id": "{01234567-ABCD-EF01-2345-6789ABCDEF01}",
        "Name": "Loopback Pseudo-Interface 1",
        "Description": "Software Loopback Interface 1",
        "NetworkInterfaceType": 24,
        "OperationalStatus": 1,
        "Speed": 1073741824,
        "IsReceiveOnly": false,
        "SupportsMulticast": true
    },
    "IPInterfaceProperties": {
        "IsDnsEnabled": false,
        "IsDynamicDnsEnabled": true,
        "DnsSuffix": "",
        "AnycastAddresses": [],
        "UnicastAddresses": [
            {
                "Address": {
                    "AddressFamily": 23,
                    "ScopeId": 0,
                    "IsIPv6Multicast": false,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false
                },
                "IPv4Mask": {
                    "AddressFamily": 2,
                    "IsIPv6Multicast": false,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false,
                    "Address": 0
                },
                "PrefixLength": 128,
                "IsTransient": false,
                "IsDnsEligible": false,
                "PrefixOrigin": 2,
                "SuffixOrigin": 2,
                "DuplicateAddressDetectionState": 4,
                "AddressValidLifetime": 4294967295,
                "AddressPreferredLifetime": 4294967295,
                "DhcpLeaseLifetime": 1542939
            },
            {
                "Address": {
                    "AddressFamily": 2,
                    "IsIPv6Multicast": false,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false,
                    "Address": 16777343
                },
                "IPv4Mask": {
                    "AddressFamily": 2,
                    "IsIPv6Multicast": false,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false,
                    "Address": 255
                },
                "PrefixLength": 8,
                "IsTransient": false,
                "IsDnsEligible": false,
                "PrefixOrigin": 2,
                "SuffixOrigin": 2,
                "DuplicateAddressDetectionState": 4,
                "AddressValidLifetime": 4294967295,
                "AddressPreferredLifetime": 4294967295,
                "DhcpLeaseLifetime": 1542939
            }
        ],
        "MulticastAddresses": [
            {
                "Address": {
                    "AddressFamily": 23,
                    "ScopeId": 1,
                    "IsIPv6Multicast": true,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false
                },
                "IsTransient": false,
                "IsDnsEligible": false,
                "PrefixOrigin": 0,
                "SuffixOrigin": 0,
                "DuplicateAddressDetectionState": 0,
                "AddressValidLifetime": 0,
                "AddressPreferredLifetime": 0,
                "DhcpLeaseLifetime": 0
            },
            {
                "Address": {
                    "AddressFamily": 2,
                    "IsIPv6Multicast": false,
                    "IsIPv6LinkLocal": false,
                    "IsIPv6SiteLocal": false,
                    "IsIPv6Teredo": false,
                    "IsIPv4MappedToIPv6": false,
                    "Address": 4211081199
                },
                "IsTransient": false,
                "IsDnsEligible": false,
                "PrefixOrigin": 0,
                "SuffixOrigin": 0,
                "DuplicateAddressDetectionState": 0,
                "AddressValidLifetime": 0,
                "AddressPreferredLifetime": 0,
                "DhcpLeaseLifetime": 0
            }
        ],
        "DnsAddresses": [
            {
                "AddressFamily": 23,
                "ScopeId": 1,
                "IsIPv6Multicast": false,
                "IsIPv6LinkLocal": false,
                "IsIPv6SiteLocal": true,
                "IsIPv6Teredo": false,
                "IsIPv4MappedToIPv6": false
            },
            {
                "AddressFamily": 23,
                "ScopeId": 1,
                "IsIPv6Multicast": false,
                "IsIPv6LinkLocal": false,
                "IsIPv6SiteLocal": true,
                "IsIPv6Teredo": false,
                "IsIPv4MappedToIPv6": false
            },
            {
                "AddressFamily": 23,
                "ScopeId": 1,
                "IsIPv6Multicast": false,
                "IsIPv6LinkLocal": false,
                "IsIPv6SiteLocal": true,
                "IsIPv6Teredo": false,
                "IsIPv4MappedToIPv6": false
            }
        ],
        "GatewayAddresses": [],
        "DhcpServerAddresses": [],
        "WinsServersAddresses": []
    },
    "IPInterfaceStatistics": {
        "OutputQueueLength": 0,
        "BytesSent": 0,
        "BytesReceived": 0,
        "UnicastPacketsSent": 0,
        "UnicastPacketsReceived": 0,
        "NonUnicastPacketsSent": 0,
        "NonUnicastPacketsReceived": 0,
        "IncomingPacketsDiscarded": 0,
        "OutgoingPacketsDiscarded": 0,
        "IncomingPacketsWithErrors": 0,
        "OutgoingPacketsWithErrors": 0,
        "IncomingUnknownProtocolPackets": 0
    },
    "IPv4InterfaceStatistics": {
        "OutputQueueLength": 0,
        "BytesSent": 0,
        "BytesReceived": 0,
        "UnicastPacketsSent": 0,
        "UnicastPacketsReceived": 0,
        "NonUnicastPacketsSent": 0,
        "NonUnicastPacketsReceived": 0,
        "IncomingPacketsDiscarded": 0,
        "OutgoingPacketsDiscarded": 0,
        "IncomingPacketsWithErrors": 0,
        "OutgoingPacketsWithErrors": 0,
        "IncomingUnknownProtocolPackets": 0
    }
}

The IgnorePropertyExceptionsResolver class comes from Ignoring class members that throw exceptions when serializing to JSON and looks like this...

using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace WebApplication1.Controllers
{
    internal class IgnorePropertyExceptionsResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var jsonProperty = base.CreateProperty(member, memberSerialization);

            jsonProperty.ShouldSerialize = instance => {
                try
                {
                    var instanceProperty = (PropertyInfo) member;

                    if (instanceProperty.CanRead)
                    {
                        instanceProperty.GetValue(instance, null);

                        return true;
                    }
                }
                catch
                {
                }

                return false;
            };

            return jsonProperty;
        }
    }
}

The reason I use this class is because some of the IPAddress objects within the collection properties of results.IPInterfaceProperties would throw an exception when their Address property was accessed, so that resolver just omits the offending properties to make the output work. Of course, you would be accessing these objects directly and not converting them to JSON first, so that class is only really needed for this demo code.

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
  • Thanks Bacon, the problem is for me I'm trying to create a global helper class that will log some network data. HttpContext doesn't exist in Azure Functions runtime, therefor I can only use it for ASP.NET Core. Thanks a log for the answer, I might simply convert Azure Function's HttpRequest to the default HttpRequest. Thanks! – NOP-MOV Oct 09 '17 at 07:11