So far I have this code:
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
{
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (IPAddressInformation uniCast in properties.UnicastAddresses)
{
// Ignore loop-back addresses & IPv6
if (!IPAddress.IsLoopback(uniCast.Address) &&
uniCast.Address.AddressFamily!= AddressFamily.InterNetworkV6)
Addresses.Add(uniCast.Address);
}
}
How can I filter the private IP addresses as well? In the same way I am filtering the loopback IP addresses.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
A more detailed response is here:
private bool _IsPrivate(string ipAddress)
{
int[] ipParts = ipAddress.Split(new String[] { "." }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => int.Parse(s)).ToArray();
// in private ip range
if (ipParts[0] == 10 ||
(ipParts[0] == 192 && ipParts[1] == 168) ||
(ipParts[0] == 172 && (ipParts[1] >= 16 && ipParts[1] <= 31))) {
return true;
}
// IP Address is probably public.
// This doesn't catch some VPN ranges like OpenVPN and Hamachi.
return false;
}
Method 2
The private address ranges are defined in RFC1918. They are:
- 10.0.0.0 – 10.255.255.255 (10/8 prefix)
- 172.16.0.0 – 172.31.255.255 (172.16/12 prefix)
- 192.168.0.0 – 192.168.255.255 (192.168/16 prefix)
You might also want to filter out link-local addresses (169.254/16) as defined in RFC3927.
Method 3
The best way to do this would be an extension method to the IP Address class
/// <summary>
/// An extension method to determine if an IP address is internal, as specified in RFC1918
/// </summary>
/// <param name="toTest">The IP address that will be tested</param>
/// <returns>Returns true if the IP is internal, false if it is external</returns>
public static bool IsInternal(this IPAddress toTest)
{
if (IPAddress.IsLoopback(toTest)) return true;
else if (toTest.ToString() == "::1") return false;
byte[] bytes = toTest.GetAddressBytes();
switch( bytes[ 0 ] )
{
case 10:
return true;
case 172:
return bytes[ 1 ] < 32 && bytes[ 1 ] >= 16;
case 192:
return bytes[ 1 ] == 168;
default:
return false;
}
}
Then, one may call the method on an instance of the IP address class
bool isIpInternal = ipAddressInformation.Address.IsInternal();
Method 4
Added IPv6 and localhost cases.
/* An IP should be considered as internal when:
::1 - IPv6 loopback
10.0.0.0 - 10.255.255.255 (10/8 prefix)
127.0.0.0 - 127.255.255.255 (127/8 prefix)
172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
*/
public bool IsInternal(string testIp)
{
if(testIp == "::1") return true;
byte[] ip = IPAddress.Parse(testIp).GetAddressBytes();
switch (ip[0])
{
case 10:
case 127:
return true;
case 172:
return ip[1] >= 16 && ip[1] < 32;
case 192:
return ip[1] == 168;
default:
return false;
}
}
Method 5
10.0.0.0 - 10.255.255.255 (10/8 prefix) 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
Use the ranges defined in the RFC (as suggested by Anders); than use regular expression to detect/remove the private IP address from the list.
Here is a sample RegEx to detect private IP addresses. (Not tested by me)
(^127.0.0.1)| (^10.)| (^172.1[6-9].)|(^172.2[0-9].)|(^172.3[0-1].)| (^192.168.)
Method 6
This implementation + tests cover:
- Loopback (IPv4, IPv6)
- Link local (IPv4, IPv6)
- Site local (IPv6)
- Unique local (IPv6, requires .NET6)
- IPv4 mapped to IPv6
Tested on .NET Core 3.1 and .NET 6.
https://gist.github.com/angularsen/f77b53ee9966fcd914025e25a2b3a085
Implementation
using System;
using System.Net;
using System.Net.Sockets;
namespace MyNamespace
{
/// <summary>
/// Extension methods on <see cref="System.Net.IPAddress"/>.
/// </summary>
public static class IPAddressExtensions
{
/// <summary>
/// Returns true if the IP address is in a private range.<br/>
/// IPv4: Loopback, link local ("169.254.x.x"), class A ("10.x.x.x"), class B ("172.16.x.x" to "172.31.x.x") and class C ("192.168.x.x").<br/>
/// IPv6: Loopback, link local, site local, unique local and private IPv4 mapped to IPv6.<br/>
/// </summary>
/// <param name="ip">The IP address.</param>
/// <returns>True if the IP address was in a private range.</returns>
/// <example><code>bool isPrivate = IPAddress.Parse("127.0.0.1").IsPrivate();</code></example>
public static bool IsPrivate(this IPAddress ip)
{
// Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4".
if (ip.IsIPv4MappedToIPv6)
ip = ip.MapToIPv4();
// Checks loopback ranges for both IPv4 and IPv6.
if (IPAddress.IsLoopback(ip)) return true;
// IPv4
if (ip.AddressFamily == AddressFamily.InterNetwork)
return IsPrivateIPv4(ip.GetAddressBytes());
// IPv6
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
return ip.IsIPv6LinkLocal ||
#if NET6_0
ip.IsIPv6UniqueLocal ||
#endif
ip.IsIPv6SiteLocal;
}
throw new NotSupportedException(
$"IP address family {ip.AddressFamily} is not supported, expected only IPv4 (InterNetwork) or IPv6 (InterNetworkV6).");
}
private static bool IsPrivateIPv4(byte[] ipv4Bytes)
{
// Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16)
bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254;
// Class A private range: 10.0.0.0 – 10.255.255.255 (10.0.0.0/8)
bool IsClassA() => ipv4Bytes[0] == 10;
// Class B private range: 172.16.0.0 – 172.31.255.255 (172.16.0.0/12)
bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31;
// Class C private range: 192.168.0.0 – 192.168.255.255 (192.168.0.0/16)
bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168;
return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB();
}
}
}
Tests
using System.Diagnostics.CodeAnalysis;
using System.Net;
using MyNamespace;
using FluentAssertions;
using Xunit;
namespace MyNamespace.Tests
{
[SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
public class IPAddressExtensionsTests
{
[Theory]
[InlineData("1.1.1.1" )] // Cloudflare DNS
[InlineData("8.8.8.8" )] // Google DNS
[InlineData("20.112.52.29")] // microsoft.com
public void IsPrivate_ReturnsFalse_PublicIPv4(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeFalse();
}
[Theory]
[InlineData("::ffff:1.1.1.1" )] // Cloudflare DNS
[InlineData("::ffff:8.8.8.8" )] // Google DNS
[InlineData("::ffff:20.112.52.29")] // microsoft.com
public void IsPrivate_ReturnsFalse_PublicIPv4MappedToIPv6(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeFalse();
}
[Theory]
[InlineData("127.0.0.1" )] // Loopback IPv4 127.0.0.1 - 127.255.255.255 (127.0.0.0/8)
[InlineData("127.10.20.30" )]
[InlineData("127.255.255.255")]
[InlineData("10.0.0.0" )] // Class A private IP 10.0.0.0 – 10.255.255.255 (10.0.0.0/8)
[InlineData("10.20.30.40" )]
[InlineData("10.255.255.255" )]
[InlineData("172.16.0.0" )] // Class B private IP 172.16.0.0 – 172.31.255.255 (172.16.0.0/12)
[InlineData("172.20.30.40" )]
[InlineData("172.31.255.255" )]
[InlineData("192.168.0.0" )] // Class C private IP 192.168.0.0 – 192.168.255.255 (192.168.0.0/16)
[InlineData("192.168.30.40" )]
[InlineData("192.168.255.255")]
[InlineData("169.254.0.0" )] // Link local (169.254.x.x)
[InlineData("169.254.30.40" )]
[InlineData("169.254.255.255")]
public void IsPrivate_ReturnsTrue_PrivateIPv4(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeTrue();
}
[Theory]
[InlineData("::ffff:127.0.0.1" )] // Loopback IPv4 127.0.0.1 - 127.255.255.254 (127.0.0.0/8)
[InlineData("::ffff:127.10.20.30" )]
[InlineData("::ffff:127.255.255.254")]
[InlineData("::ffff:10.0.0.0" )] // Class A private IP 10.0.0.0 – 10.255.255.255 (10.0.0.0/8)
[InlineData("::ffff:10.20.30.40" )]
[InlineData("::ffff:10.255.255.255" )]
[InlineData("::ffff:172.16.0.0" )] // Class B private IP 172.16.0.0 – 172.31.255.255 (172.16.0.0/12)
[InlineData("::ffff:172.20.30.40" )]
[InlineData("::ffff:172.31.255.255" )]
[InlineData("::ffff:192.168.0.0" )] // Class C private IP 192.168.0.0 – 192.168.255.255 (192.168.0.0/16)
[InlineData("::ffff:192.168.30.40" )]
[InlineData("::ffff:192.168.255.255")]
[InlineData("::ffff:169.254.0.0" )] // Link local (169.254.x.x)
[InlineData("::ffff:169.254.30.40" )]
[InlineData("::ffff:169.254.255.255")]
public void IsPrivate_ReturnsTrue_PrivateIPv4MappedToIPv6(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeTrue();
}
[Theory]
[InlineData("::1" )] // Loopback
[InlineData("fe80::" )] // Link local
[InlineData("fe80:1234:5678::1")] // Link local
[InlineData("fc00::" )] // Unique local, globally assigned.
[InlineData("fc00:1234:5678::1")] // Unique local, globally assigned.
[InlineData("fd00::" )] // Unique local, locally assigned.
[InlineData("fd12:3456:789a::1")] // Unique local, locally assigned.
public void IsPrivate_ReturnsTrue_PrivateIPv6(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeTrue();
}
[Theory]
[InlineData("2606:4700:4700::64" )] // Cloudflare DNS
[InlineData("2001:4860:4860::8888" )] // Google DNS
[InlineData("2001:0db8:85a3:0000:0000:8a2e:0370:7334")] // Commonly used example.
public void IsPrivate_ReturnsFalse_PublicIPv6(string ip)
{
var ipAddress = IPAddress.Parse(ip);
IPAddressExtensions.IsPrivate(ipAddress).Should().BeFalse();
}
}
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0