Подтвердить что ты не робот

Получить Шлюз по умолчанию

Я пишу программу, которая показывает пользователю свой IP-адрес, маску подсети и шлюз по умолчанию. Я могу получить первые два, но для последнего, это то, что я обнаружил:

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

Это, конечно, возвращает коллекцию GatewayIPAddressInformation. Итак, если компьютер имеет несколько шлюзов, как я могу определить, какой шлюз по умолчанию?

На практике я только видел, что в этой коллекции содержится одна запись, но поскольку она реализована как коллекция, то понятно, что на некоторых компьютерах есть несколько шлюзов, ни одна из которых не помечена как "По умолчанию". Итак, есть ли способ определить значение по умолчанию или все это просто догадки?

4b9b3361

Ответ 1

Это должен быть первый действительный и включенный адрес шлюза первого включенного сетевого интерфейса:

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 чтобы различать 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;
}

Ответ 2

Первый IP-адрес, возвращаемый командой traceroute, будет шлюзом. Вы также можете использовать этот факт для получения шлюза. Здесь вы можете найти приятную реализацию tracerout: TraceRoute и Ping на С#

Ответ 3

Я знаю, что это немного более старый вопрос, но я только что пришел к необходимости получить 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;
    }

Ответ 4

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());

Ответ 5

в соответствии с комментарием @midspace на @caesay ответ, это лучший ответ:

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

Ответ 6

Я только что наткнулся на это и буду использовать следующий код - в основном он ищет интерфейсы, которые не замыкаются на петлю и не работают, и имеют шлюзы и одноадресные адреса. Из интерфейса я получаю непрерывный 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;
    }
}

Ответ 7

Ищете один странный трюк для определения локального 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, но не будет отправлять данные по нему. Затем вы можете увидеть, к какому локальному интерфейсу и адресу был привязан сокет. Вы должны указать IP-адрес или имя хоста, на котором я использовал google.com, который будет использоваться ОС для определения того, к какому интерфейсу будет привязан сокет и какой IP-адрес вы найдете с помощью этого метода. Для 99% + IP-адресов или имен хостов, которые вы используете, вы получите интерфейс + адрес, который будет использоваться для трафика по маршруту по умолчанию.

Хотя это немного глупо, я подозреваю, что это более надежно, чем описанные выше методы, когда люди используют вызов PInvoke и сканируют структуры, чтобы увидеть, могут ли они найти интерфейс.

Я определенно нашел, что это более надежно в моих системах с двойным интерфейсом 3x, чем сканирование по результатам NetworkInterface.GetAllNetworkInterfaces() с использованием эвристики, чтобы попытаться выяснить адрес интерфейса по умолчанию.

[Обновление/ответ на @caesay 6/6/2019] @caesay Сделал отличное замечание, поэтому я переключил пример с использования UdpClient на Socket и явно вызвал Socket.Connect() после Bind(). Я также рассмотрел исходный код Framework для Connect() и LocalEndPoint, и оба они немедленно вызывают соответствующий системный вызов ОС Win32, не откладывая и не ленивый при вызове в ОС. Страница связывания Win32 говорит: "Приложение может использовать getsockname после вызова связывания, чтобы узнать адрес и порт, который был назначен сокету. Если интернет-адрес равен INADDR_ANY или in6addr_any, getsockname не может обязательно указывать адрес, пока сокет не будет связано". Это условие теперь явно выполняется, и после проверки источника Framework IP-адрес всегда должен быть доступен после вызова Connect().

Ответ 8

Я думаю, что вы должны перебирать эту коллекцию и показывать все шлюзы, поскольку, если есть много шлюзов для адаптера, все они считаются по умолчанию для этого адаптера