J'écris un programme qui montre à l'utilisateur son adresse IP, son masque de sous-réseau et sa passerelle par défaut. Je peux obtenir les deux premiers, mais pour le dernier, voici ce que j'ai découvert:
GatewayIPAddressInformationCollection gwc =
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;
Cela, bien sûr, renvoie une collection de GatewayIPAddressInformation
. Donc, si un ordinateur possède plusieurs passerelles, comment puis-je déterminer quelle est la passerelle par défaut?
En pratique, je n'ai vu que cette collection contenir une seule entrée, mais comme elle est implémentée en tant que collection, il va de soi que certains ordinateurs contiennent plusieurs passerelles, dont aucune n'est marquée comme "par défaut". Alors, existe-t-il un moyen de déterminer le par défaut ou est-ce juste une conjecture?
Il doit s'agir de la première adresse de passerelle valide et activée de la première interface réseau activée:
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();
}
J'ai également ajouté quelques vérifications commentées supplémentaires qui ont été signalées comme utiles par d'autres personnes ici. Vous pouvez vérifier le AddressFamily
pour faire la distinction entre IPv4 et IPv6. Ce dernier peut être utilisé si vous rencontrez des problèmes avec un 0.0.0.0
adresse renvoyée.
Cela dit, la méthode recommandée consiste à utiliser GetBestInterface
pour trouver une interface de routage vers une adresse IP spécifique. Si vous avez déjà une adresse IP de destination à l'esprit, il est préférable de l'utiliser - et j'ai donc également inclus un exemple de cela ci-dessous:
[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;
}
La première adresse IP renvoyée par la commande traceroute
sera la passerelle. Vous pouvez également utiliser ce fait pour obtenir la passerelle.Vous pouvez trouver une implémentation Nice de tracerout
ici: TraceRoute et Ping en C #
Je sais que c'est une question un peu plus ancienne mais, je venais de découvrir la nécessité de récupérer l'adresse IPV4 de la passerelle locale. La réponse acceptée ne correspond pas tout à fait à la loi en ce qui concerne mon propre système, je l'ai donc modifié en suite et je suis sûr que d'autres pourront également utiliser cette solution.
Comme je n'ai pas encore assez de représentants pour commenter, je suis obligé d'ajouter ceci comme une "question":
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;
}
Vous cherchez une astuce étrange pour déterminer l'adresse IP locale? Mais le fait-il de manière très robuste par rapport aux méthodes antérieures?
[Voir la fin pour la réponse à @caesay]
C'est peut-être ce que vous recherchez.
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);
}
Cela créera un socket UDP, mais n'enverra pas de données dessus. Vous pouvez alors voir à quelle interface locale et à quelle adresse le socket était lié. Vous devez fournir une adresse IP ou un nom d'hôte où j'ai utilisé google.com, qui sera utilisé par le système d'exploitation pour déterminer à quelle interface le socket sera lié et à quelle adresse IP vous trouverez en utilisant cette méthode. Pour 99% + des adresses IP ou des noms d'hôte que vous utilisez, vous obtiendrez l'interface + l'adresse à utiliser pour le trafic sur la route par défaut.
Bien que cela soit un peu obtus, je soupçonne que c'est plus fiable que les méthodes ci-dessus où les gens utilisent l'appel PInvoke et parcourent les structures pour voir s'ils peuvent trouver l'interface.
Je l'ai certainement trouvé plus robuste sur mes systèmes à double interface 3x que de parcourir les résultats de NetworkInterface.GetAllNetworkInterfaces () en utilisant une heuristique pour essayer de comprendre l'adresse d'interface par défaut.
[Mise à jour/réponse à @caesay 2/6/2019] @caesay a fait un excellent point ici, j'ai donc changé l'exemple de l'utilisation d'UdpClient en Socket, et j'ai explicitement appelé Socket.Connect () après Bind (). J'ai également examiné la source Framework pour Connect () et LocalEndPoint, et les deux appellent immédiatement leur appel système Win32 OS correspondant sans différer ni être paresseux à appeler dans le système d'exploitation. La page de liaison Win32 indique "L'application peut utiliser getsockname après avoir appelé bind pour connaître l'adresse et le port qui ont été attribués au socket. Si l'adresse Internet est égale à INADDR_ANY ou in6addr_any, getsockname ne peut pas nécessairement fournir l'adresse tant que le socket n'est pas lié". Cette condition est désormais explicitement traitée et, après avoir examiné la source du Framework, l'adresse IP doit toujours être disponible après l'appel Connect ().
Je viens de découvrir cela et utiliserai le code suivant - en gros, il recherche des interfaces qui ne sont pas en boucle et qui sont actives et qui ont des passerelles et des adresses de monodiffusion. De l'interface, j'obtiens alors une adresse IPV4 non transitoire.
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;
}
}
selon le commentaire de @ midspace sur la réponse de @ caesay, c'est une meilleure réponse:
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;
}
Je vous préviens que ce n'est pas une solution complète, si vous cherchez l'interface principale responsable de l'accès à Internet, vous devez combiner d'autres approches comme l'utilisation de l'API win32 GetBestInterface pour trouver la meilleure interface pour se connecter à une adresse de destination.
vous pouvez trouver un exemple d'utilisation ici: http://www.pinvoke.net/default.aspx/iphlpapi.getbestinterface
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());
Je pense que vous devez parcourir cette collection et afficher toutes les passerelles, car s'il existe de nombreuses passerelles vers un adaptateur, elles sont toutes considérées comme par défaut pour cet adaptateur.