#if HAVE_RT_MSGHDR
int32_t SystemNative_EnumerateGatewayAddressesForInterface(uint32_t interfaceIndex, GatewayAddressFound onGatewayFound)
{
- int routeDumpName[] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+ static struct in6_addr anyaddr = IN6ADDR_ANY_INIT;
+ int routeDumpName[] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_DUMP, 0};
size_t byteCount;
int isGateway = flags & RTF_GATEWAY;
int gatewayPresent = hdr->rtm_addrs & RTA_GATEWAY;
- if (isGateway && gatewayPresent)
+ if (isGateway && gatewayPresent && ((int)interfaceIndex == -1 || interfaceIndex == hdr->rtm_index))
{
IpAddressInfo iai;
+ struct sockaddr_storage* sock = (struct sockaddr_storage*)(hdr + 1);
memset(&iai, 0, sizeof(IpAddressInfo));
- iai.InterfaceIndex = interfaceIndex;
- iai.NumAddressBytes = NUM_BYTES_IN_IPV4_ADDRESS;
- struct sockaddr_in* sain = (struct sockaddr_in*)(hdr + 1);
- sain = sain + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
- memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain->sin_addr.s_addr, sizeof(sain->sin_addr.s_addr));
+ iai.InterfaceIndex = hdr->rtm_index;
+
+ if (sock->ss_family == AF_INET)
+ {
+ iai.NumAddressBytes = NUM_BYTES_IN_IPV4_ADDRESS;
+ struct sockaddr_in* sain = (struct sockaddr_in*)sock;
+ if (sain->sin_addr.s_addr != 0)
+ {
+ // filter out normal routes.
+ continue;
+ }
+
+ sain = sain + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
+ memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain->sin_addr.s_addr, sizeof(sain->sin_addr.s_addr));
+ }
+ else if (sock->ss_family == AF_INET6)
+ {
+ struct sockaddr_in6* sain6 = (struct sockaddr_in6*)sock;
+ iai.NumAddressBytes = NUM_BYTES_IN_IPV6_ADDRESS;
+ if (memcmp(&anyaddr, &sain6->sin6_addr, sizeof(sain6->sin6_addr)) != 0)
+ {
+ // filter out normal routes.
+ continue;
+ }
+
+ sain6 = sain6 + 1; // Skip over the first sockaddr, the destination address. The second is the gateway.
+ if ((sain6->sin6_addr.__u6_addr.__u6_addr16[0] & htons(0xfe80)) == htons(0xfe80))
+ {
+ // clear embedded if index.
+ sain6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
+ }
+
+ memcpy_s(iai.AddressBytes, sizeof_member(IpAddressInfo, AddressBytes), &sain6->sin6_addr, sizeof(sain6->sin6_addr));
+ }
+ else
+ {
+ // Ignore other address families.
+ continue;
+ }
onGatewayFound(&iai);
}
}
Buffer.MemoryCopy(gatewayAddressInfo->AddressBytes, ipArrayPtr, ipBytes.Length, ipBytes.Length);
}
IPAddress ipAddress = new IPAddress(ipBytes);
+ if (ipAddress.IsIPv6LinkLocal)
+ {
+ // For Link-Local addresses add ScopeId as that is not part of the route entry.
+ ipAddress.ScopeId = interfaceIndex;
+ }
addressSet.Add(ipAddress);
}) == -1)
{
private readonly bool _supportsMulticast;
private readonly long _speed;
- protected unsafe BsdNetworkInterface(string name) : base(name)
+ protected unsafe BsdNetworkInterface(string name, int index) : base(name)
{
+ _index = index;
Interop.Sys.NativeIPInterfaceStatistics nativeStats;
if (Interop.Sys.GetNativeIPInterfaceStatistics(name, out nativeStats) == -1)
{
{
try
{
- BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+ BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
oni.ProcessIpv4Address(ipAddr, maskAddr);
}
catch (Exception e)
{
try
{
- BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+ BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
oni.ProcessIpv6Address(ipAddr, *scopeId);
}
catch (Exception e)
{
try
{
- BsdNetworkInterface oni = GetOrCreate(interfacesByName, name);
+ BsdNetworkInterface oni = GetOrCreate(interfacesByName, name, llAddr->InterfaceIndex);
oni.ProcessLinkLayerAddress(llAddr);
}
catch (Exception e)
/// </summary>
/// <param name="interfaces">The Dictionary of existing interfaces.</param>
/// <param name="name">The name of the interface.</param>
+ /// <param name="index">Interface index of the interface.</param>
/// <returns>The cached or new BsdNetworkInterface with the given name.</returns>
- private static BsdNetworkInterface GetOrCreate(Dictionary<string, BsdNetworkInterface> interfaces, string name)
+ private static BsdNetworkInterface GetOrCreate(Dictionary<string, BsdNetworkInterface> interfaces, string name, int index)
{
BsdNetworkInterface oni;
if (!interfaces.TryGetValue(name, out oni))
{
- oni = new BsdNetworkInterface(name);
+ oni = new BsdNetworkInterface(name, index);
interfaces.Add(name, oni);
}
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.IO;
namespace System.Net.NetworkInformation
{
// and separates the information about by each interface.
public GatewayIPAddressInformationCollection GetGatewayAddresses()
{
- List<GatewayIPAddressInformation> innerCollection
- = StringParsingHelpers.ParseGatewayAddressesFromRouteFile(NetworkFiles.Ipv4RouteFile, _linuxNetworkInterface.Name);
- return new GatewayIPAddressInformationCollection(innerCollection);
+ List<GatewayIPAddressInformation> collection = new List<GatewayIPAddressInformation>();
+
+ if (File.Exists(NetworkFiles.Ipv4RouteFile))
+ {
+ StringParsingHelpers.ParseIPv4GatewayAddressesFromRouteFile(collection, NetworkFiles.Ipv4RouteFile, _linuxNetworkInterface.Name);
+ }
+
+ if (File.Exists(NetworkFiles.Ipv6RouteFile))
+ {
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(collection, NetworkFiles.Ipv6RouteFile, _linuxNetworkInterface.Name, _linuxNetworkInterface.Index);
+ }
+
+ return new GatewayIPAddressInformationCollection(collection);
}
private IPAddressCollection GetDhcpServerAddresses()
private readonly long? _speed;
private readonly LinuxIPInterfaceProperties _ipProperties;
- internal LinuxNetworkInterface(string name) : base(name)
+ internal LinuxNetworkInterface(string name, int index) : base(name)
{
+ _index = index;
_operationalStatus = GetOperationalStatus(name);
_supportsMulticast = GetSupportsMulticast(name);
_speed = GetSpeed(name);
Dictionary<string, LinuxNetworkInterface> interfacesByName = new Dictionary<string, LinuxNetworkInterface>();
List<Exception> exceptions = null;
const int MaxTries = 3;
+
for (int attempt = 0; attempt < MaxTries; attempt++)
{
// Because these callbacks are executed in a reverse-PInvoke, we do not want any exceptions
- // to propogate out, because they will not be catchable. Instead, we track all the exceptions
+ // to propagate out, because they will not be catchable. Instead, we track all the exceptions
// that are thrown in these callbacks, and aggregate them at the end.
int result = Interop.Sys.EnumerateInterfaceAddresses(
(name, ipAddr, maskAddr) =>
{
try
{
- LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+ LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
lni.ProcessIpv4Address(ipAddr, maskAddr);
}
catch (Exception e)
{
try
{
- LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+ LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, ipAddr->InterfaceIndex);
lni.ProcessIpv6Address(ipAddr, *scopeId);
}
catch (Exception e)
{
try
{
- LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name);
+ LinuxNetworkInterface lni = GetOrCreate(interfacesByName, name, llAddr->InterfaceIndex);
lni.ProcessLinkLayerAddress(llAddr);
}
catch (Exception e)
/// </summary>
/// <param name="interfaces">The Dictionary of existing interfaces.</param>
/// <param name="name">The name of the interface.</param>
+ /// <param name="index">Interafce index of the interface.</param>
/// <returns>The cached or new LinuxNetworkInterface with the given name.</returns>
- private static LinuxNetworkInterface GetOrCreate(Dictionary<string, LinuxNetworkInterface> interfaces, string name)
+ private static LinuxNetworkInterface GetOrCreate(Dictionary<string, LinuxNetworkInterface> interfaces, string name, int index)
{
LinuxNetworkInterface lni;
if (!interfaces.TryGetValue(name, out lni))
{
- lni = new LinuxNetworkInterface(name);
+ lni = new LinuxNetworkInterface(name, index);
interfaces.Add(name, lni);
}
{
internal static partial class StringParsingHelpers
{
+ private static char[] s_delimiter = new char[1] { ' ' };
// /proc/net/route contains some information about gateway addresses,
// and separates the information about by each interface.
- internal static List<GatewayIPAddressInformation> ParseGatewayAddressesFromRouteFile(string filePath, string interfaceName)
+ internal static List<GatewayIPAddressInformation> ParseIPv4GatewayAddressesFromRouteFile(List<GatewayIPAddressInformation> collection, string filePath, string interfaceName)
{
- if (!File.Exists(filePath))
- {
- throw ExceptionHelper.CreateForInformationUnavailable();
- }
-
- List<GatewayIPAddressInformation> collection = new List<GatewayIPAddressInformation>();
// Columns are as follows (first-line header):
// Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
string[] fileLines = File.ReadAllLines(filePath);
parser.MoveNextOrFail();
string gatewayIPHex = parser.MoveAndExtractNext();
long addressValue = Convert.ToInt64(gatewayIPHex, 16);
- IPAddress address = new IPAddress(addressValue);
- collection.Add(new SimpleGatewayIPAddressInformation(address));
+ if (addressValue != 0)
+ {
+ // Skip device routes without valid NextHop IP address.
+ IPAddress address = new IPAddress(addressValue);
+ collection.Add(new SimpleGatewayIPAddressInformation(address));
+ }
}
}
return collection;
}
+ internal static void ParseIPv6GatewayAddressesFromRouteFile(List<GatewayIPAddressInformation> collection, string filePath, string interfaceName, long scopeId)
+ {
+ // Columns are as follows (first-line header):
+ // 00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 00000001 00200200 lo
+ // +------------------------------+ ++ +------------------------------+ ++ +------------------------------+ +------+ +------+ +------+ +------+ ++
+ // | | | | | | | | | |
+ // 0 1 2 3 4 5 6 7 8 9
+ //
+ // 0. IPv6 destination network displayed in 32 hexadecimal chars without colons as separator
+ // 1. IPv6 destination prefix length in hexadecimal
+ // 2. IPv6 source network displayed in 32 hexadecimal chars without colons as separator
+ // 3. IPv6 source prefix length in hexadecimal
+ // 4. IPv6 next hop displayed in 32 hexadecimal chars without colons as separator
+ // 5. Metric in hexadecimal
+ // 6. Reference counter
+ // 7. Use counter
+ // 8. Flags
+ // 9. Interface name
+ string[] fileLines = File.ReadAllLines(filePath);
+ foreach (string line in fileLines)
+ {
+ if (line.StartsWith("00000000000000000000000000000000"))
+ {
+ string[] token = line.Split(s_delimiter, StringSplitOptions.RemoveEmptyEntries);
+ if (token.Length > 9 && token[4] != "00000000000000000000000000000000")
+ {
+ if (!string.IsNullOrEmpty(interfaceName) && interfaceName != token[9])
+ {
+ continue;
+ }
+
+ IPAddress address = ParseIPv6HexString(token[4], isNetworkOrder: true);
+ if (address.IsIPv6LinkLocal)
+ {
+ // For Link-Local addresses add ScopeId as that is not part of the route entry.
+ address.ScopeId = scopeId;
+ }
+ collection.Add(new SimpleGatewayIPAddressInformation(address));
+ }
+ }
+ }
+ }
+
internal static List<IPAddress> ParseDhcpServerAddressesFromLeasesFile(string filePath, string name)
{
// Parse the /var/lib/dhcp/dhclient.leases file, if it exists.
}
// Parses a 128-bit IPv6 Address stored as 4 concatenated 32-bit hex numbers.
+ // If isSequence is true it assumes that hexAddress is in sequence of IPv6 bytes.
// First number corresponds to lower address part
// E.g. IP-address: fe80::215:5dff:fe00:402
// It's bytes in direct order: FE-80-00-00 00-00-00-00 02-15-5D-FF FE-00-04-02
// It's represenation in /proc/net/tcp6: 00-00-80-FE 00-00-00-00 FF-5D-15-02 02-04-00-FE
// (dashes and spaces added above for readability)
// Strings passed to this must be 32 characters in length.
- private static IPAddress ParseIPv6HexString(string hexAddress)
+ private static IPAddress ParseIPv6HexString(string hexAddress, bool isNetworkOrder = false)
{
Debug.Assert(hexAddress.Length == 32);
byte[] addressBytes = new byte[16];
- for (int i = 0; i < 4; i++)
+ if (isNetworkOrder)
{
- for (int j = 0; j < 4; j++)
+ for (int i = 0; i < 16; i++)
{
- int srcIndex = i * 4 + 3 - j;
- int targetIndex = i * 4 + j;
- addressBytes[targetIndex] = (byte)(HexToByte(hexAddress[srcIndex * 2]) * 16
- + HexToByte(hexAddress[srcIndex * 2 + 1]));
+ addressBytes[i] = (byte)(HexToByte(hexAddress[(i * 2)]) * 16
+ + HexToByte(hexAddress[(i * 2) + 1]));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ int srcIndex = i * 4 + 3 - j;
+ int targetIndex = i * 4 + j;
+ addressBytes[targetIndex] = (byte)(HexToByte(hexAddress[srcIndex * 2]) * 16
+ + HexToByte(hexAddress[srcIndex * 2 + 1]));
+ }
}
}
}
[Fact]
- public void GatewayAddressParsing()
+ public void IPv4GatewayAddressParsing()
{
string fileName = GetTestFilePath();
FileUtil.NormalizeLineEndings("NetworkFiles/route", fileName);
- List<GatewayIPAddressInformation> gatewayAddresses = StringParsingHelpers.ParseGatewayAddressesFromRouteFile(fileName, "wlan0");
+ List<GatewayIPAddressInformation> gatewayAddresses = new List<GatewayIPAddressInformation>();
+ StringParsingHelpers.ParseIPv4GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "wlan0");
Assert.Equal(3, gatewayAddresses.Count);
Assert.Equal(IPAddress.Parse("10.105.128.1"), gatewayAddresses[0].Address);
Assert.Equal(IPAddress.Parse("152.186.220.254"), gatewayAddresses[2].Address);
}
+ [Fact]
+ public void IPv6GatewayAddressParsing()
+ {
+ string fileName = GetTestFilePath();
+ FileUtil.NormalizeLineEndings("NetworkFiles/ipv6_route", fileName);
+ List<GatewayIPAddressInformation> gatewayAddresses = new List<GatewayIPAddressInformation>();
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "lo", 42);
+ Assert.Equal(0, gatewayAddresses.Count);
+
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "foo", 42);
+ Assert.Equal(0, gatewayAddresses.Count);
+
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "enp0s5", 42);
+ Assert.Equal(2, gatewayAddresses.Count);
+
+ Assert.Equal(IPAddress.Parse("2002:2c26:f4e4:0:21c:42ff:fe20:4636"), gatewayAddresses[0].Address);
+ Assert.Equal(IPAddress.Parse("fe80::21c:42ff:fe00:18%42"), gatewayAddresses[1].Address);
+
+ gatewayAddresses = new List<GatewayIPAddressInformation>();
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, "wlan0", 21);
+ Assert.Equal(IPAddress.Parse("fe80::21c:42ff:fe00:18%21"), gatewayAddresses[0].Address);
+
+ gatewayAddresses = new List<GatewayIPAddressInformation>();
+ StringParsingHelpers.ParseIPv6GatewayAddressesFromRouteFile(gatewayAddresses, fileName, null, 0);
+ Assert.Equal(3, gatewayAddresses.Count);
+ }
+
[Fact]
public void DhcpServerAddressParsing()
{
fe80000000000000aefdcefffe7ca67d 80 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000001 00000000 80200001 lo
ff000000000000000000000000000000 08 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 wlan0
00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 ffffffff 00000001 0000000f 00200200 lo
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 20022c26f4e40000021c42fffe204636 00000400 00000002 00000087 00450003 enp0s5
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021c42fffe000018 00000400 00000002 00000087 00450003 enp0s5
+00000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021c42fffe000018 00000400 00000002 00000087 00450003 wlan0