web-dev-qa-db-ja.com

デフォルトゲートウェイを取得する

IPアドレス、サブネットマスク、デフォルトゲートウェイをユーザーに表示するプログラムを作成しています。最初の2つは取得できますが、最後の1つについては次のようになりました。

GatewayIPAddressInformationCollection gwc = 
    System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;

もちろん、GatewayIPAddressInformationのコレクションを返します。コンピュータに複数のゲートウェイがある場合、どのゲートウェイがデフォルトであるかをどのように判断できますか?

実際には、このコレクションに単一のエントリが含まれているのを見たことがありますが、コレクションとして実装されているため、一部のコンピューターに複数のゲートウェイが含まれ、そのいずれも「デフォルト」としてマークされていません。 デフォルトを決定する方法はありますか、それともすべて推測だけですか?

26
Frecklefoot

最初に有効になっているネットワークインターフェイスの最初の有効で有効なゲートウェイアドレスである必要があります。

public static IPAddress GetDefaultGateway()
{
    return NetworkInterface
        .GetAllNetworkInterfaces()
        .Where(n => n.OperationalStatus == OperationalStatus.Up)
        .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
        .SelectMany(n => n.GetIPProperties()?.GatewayAddresses)
        .Select(g => g?.Address)
        .Where(a => a != null)
         // .Where(a => a.AddressFamily == AddressFamily.InterNetwork)
         // .Where(a => Array.FindIndex(a.GetAddressBytes(), b => b != 0) >= 0)
        .FirstOrDefault();
}

また、ここで他の人から有用であると指摘されたコメント付きのチェックをいくつか追加しました。 AddressFamily oneをチェックして、IPv4とIPv6を区別できます。返される0.0.0.0アドレスに問題がある場合は、後者を使用できます。

ただし、これを行うための推奨される方法では、 GetBestInterface を使用して、特定のIPアドレスにルーティングするためのインターフェースを見つけます。すでに宛先IPアドレスを念頭に置いている場合は、これを使用することをお勧めします。そのため、以下の例も含めました。

[DllImport("iphlpapi.dll", CharSet = CharSet.Auto)]
private static extern int GetBestInterface(UInt32 destAddr, out UInt32 bestIfIndex);

public static IPAddress GetGatewayForDestination(IPAddress destinationAddress)
{
    UInt32 destaddr = BitConverter.ToUInt32(destinationAddress.GetAddressBytes(), 0);

    uint interfaceIndex;
    int result = GetBestInterface(destaddr, out interfaceIndex);
    if (result != 0)
        throw new Win32Exception(result);

    foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
    {
        var niprops = ni.GetIPProperties();
        if (niprops == null)
            continue;

        var gateway = niprops.GatewayAddresses?.FirstOrDefault()?.Address;
        if (gateway == null)
            continue;

        if (ni.Supports(NetworkInterfaceComponent.IPv4))
        {
            var v4props = niprops.GetIPv4Properties();
            if (v4props == null)
                continue;

            if (v4props.Index == interfaceIndex)
                return gateway;
        }

        if (ni.Supports(NetworkInterfaceComponent.IPv6))
        {
            var v6props = niprops.GetIPv6Properties();
            if (v6props == null)
                continue;

            if (v6props.Index == interfaceIndex)
                return gateway;
        }
    }

    return null;
}
38
caesay

これは少し古い質問ですが、ローカルゲートウェイのIPV4アドレスを取得する必要に迫られました。私のシステムに関しては、受け入れられた答えは法案に合わないので、それをスイートに変更し、他の人もこのソリューションを使用できると確信しています。

コメントするのに十分な担当者がまだいないので、これを「質問」として追加せざるを得ません。

public static IPAddress GetDefaultGateway()
    {
        IPAddress result = null;
        var cards = NetworkInterface.GetAllNetworkInterfaces().ToList();
        if (cards.Any())
        {
            foreach (var card in cards)
            {
                var props = card.GetIPProperties();
                if (props == null)
                    continue;

                var gateways = props.GatewayAddresses;
                if (!gateways.Any())
                    continue;

                var gateway =
                    gateways.FirstOrDefault(g => g.Address.AddressFamily.ToString() == "InterNetwork");
                if (gateway == null)
                    continue;

                result = gateway.Address;
                break;
            };
        }

        return result;
    }
2
Code Jockey

ローカルIPアドレスを特定するための奇妙なトリックを探していますか?しかし、以前の方法と比べて非常に堅牢ですか?

[@caesayへの応答については、最後をご覧ください]

これはあなたが探しているものかもしれません。

using (var s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp))
{
    s.Bind( new System.Net.IPEndPoint(System.Net.IPAddress.Any,0));
    s.Connect("google.com",0);

    var ipaddr = s.LocalEndPoint as System.Net.IPEndPoint;
    var addr = ipaddr.Address.ToString();
    Console.WriteLine(addr);
}

これにより、UDPソケットが作成されますが、データは送信されません。その後、ソケットがバインドされているローカルインターフェイスとアドレスを確認できます。 google.comを使用したIPアドレスまたはホスト名を指定する必要があります。これは、ソケットがバインドされるインターフェイスと、この方法を使用して検出されるIPアドレスを決定するためにOSによって使用されます。使用するIPアドレスまたはホスト名の99%以上については、デフォルトルート上のトラフィックに使用されるinterface + addressを取得します。

これは少しわかりにくいですが、PInvoke呼び出しを使用して構造をスキャンしてインターフェイスを見つけることができるかどうかを確認する上記の方法よりも信頼性が高いと思われます。

確かに、3xダブルインターフェイスシステムでは、ヒューリスティックを使用してNetworkInterface.GetAllNetworkInterfaces()の結果をスキャンしてデフォルトのインターフェイスアドレスを見つけ出すよりも、より堅牢であることがわかりました。

[@caesay 2/6/2019への更新/応答] @caesayはここで優れた点を示しているため、サンプルをUdpClientからSocketに切り替え、Bind()の後に明示的にSocket.Connect()を呼び出しました。また、Connect()およびLocalEndPointのフレームワークソースを確認しました。どちらも、OSへの呼び出しを延期したり怠けたりすることなく、対応するWin32 OSシステムコールをすぐに呼び出します。 Win32バインドページには、「バインドを呼び出した後、アプリケーションはgetsocknameを使用して、ソケットに割り当てられたアドレスとポートを学習できます。インターネットアドレスがINADDR_ANYまたはin6addr_anyに等しい場合、getsocknameは、ソケットが接続済み」。この状態は現在明示的に対処されており、フレームワークのソースを確認したため、Connect()呼び出し後は常にIPアドレスが利用可能になっているはずです。

2
Cameron

私はこれに出会ったばかりで、次のコードを使用します-基本的には、ループバックではなく、アップ状態であり、ゲートウェイとユニキャストアドレスを持つインターフェイスを探します。インターフェイスから、非一時的なIPV4アドレスを取得します。

public static class NetworkInterfaces
{
    public static NetworkInterface GetDefaultInterface()
    {
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
        foreach (var intf in interfaces)
        {
            if (intf.OperationalStatus != OperationalStatus.Up)
            {
                continue;
            }
            if (intf.NetworkInterfaceType == NetworkInterfaceType.Loopback)
            {
                continue;
            }

            var properties = intf.GetIPProperties();
            if (properties == null)
            {
                continue;
            }
            var gateways = properties.GatewayAddresses;
            if ((gateways == null) || (gateways.Count == 0))
            {
                continue;
            }
            var addresses = properties.UnicastAddresses;
            if ((addresses == null) || (addresses.Count == 0))
            {
                continue;
            }
            return intf;
        }
        return null;
    }

    public static IPAddress GetDefaultIPV4Address(NetworkInterface intf)
    {
        if (intf == null)
        {
            return null;
        }
        foreach (var address in intf.GetIPProperties().UnicastAddresses)
        {
            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
            {
                continue;
            }
            if (address.IsTransient)
            {
                continue;
            }
            return address.Address;
        }
        return null;
    }
}
1
Nick Randell

@caesayの答えに対する@midspaceのコメントによれば、これはより良い答えです:

public static IPAddress GetDefaultGateway()
{
    var gateway_address = NetworkInterface.GetAllNetworkInterfaces()
        .Where(e => e.OperationalStatus == OperationalStatus.Up)
        .SelectMany(e => e.GetIPProperties().GatewayAddresses)
        .FirstOrDefault();
    if (gateway_address == null) return null;
    return gateway_address.Address;
}

完全なソリューションではないことを警告しています。インターネットアクセスを担当するメインインターフェイスを探している場合は、win32 API GetBestInterface を使用するなど、他のアプローチを組み合わせて、接続に最適なインターフェイスを見つける必要があります宛先アドレスへ。

ここで使用例を見つけることができます: http://www.pinvoke.net/default.aspx/iphlpapi.getbestinterface

1
zero.zero.seven
NetworkInterface[] allNICs = NetworkInterface.GetAllNetworkInterfaces();
foreach (var nic in allNICs)
{
    var ipProp = nic.GetIPProperties();
    var gwAddresses = ipProp.GatewayAddresses;
    if (gwAddresses.Count > 0 &&
        gwAddresses.FirstOrDefault(g => g.Address.AddressFamily == AddressFamily.InterNetwork) != null)
    {
        localIP = ipProp.UnicastAddresses.First(d => d.Address.AddressFamily == AddressFamily.InterNetwork).Address;
    }
}
Debug.Print(localIP.ToString());
1
Wayne

アダプタへのゲートウェイが多数ある場合、それらはすべてそのアダプタのデフォルトと見なされるため、このコレクションを反復処理してすべてのゲートウェイを表示する必要があると思います

0
Falcon313