0

I have a large dynamic list of IP Addresses, their subnet masks, and a given set of subnets. I need some code to be able to identify which subnet each address is on. I'm keeping this fairly vague because I'm open to many different solutions. Does anybody have some code in C# that can do something similar to this? Thanks!

3 Answers3

2

First, you need code to mask an address to its subnet:

public IPAddress ApplyMask(IPAddress address, IPAddress mask) {
    byte[] addressBytes = address.GetAddressBytes();
    byte[] maskBytes = mask.GetAddressBytes();
    var appliedMaskBytes =
        addressBytes.Zip(
            maskBytes,
            (addressByte, maskByte) => (byte)(addressByte & maskByte)
        )
        .ToArray();
    return new IPAddress(appliedMaskBytes);
}

Then, assuming you have a Dictionary<IPAddress, IPAddress> from an IP address to its mask:

IDictionary<IPAddress, IPAddress> maskDictionary;
var masked = ipAddresses
                 .Select(ipAddress => 
                     new { IPAddress = ipAddress,
                           Subnet = ApplyMask(
                               ipAddress,
                               maskDictionary[ipAddress]
                           )
                     }
                 )
                 .GroupBy(x => x.Subnet);

Now you have a list of address by subnet. Proceed accordingly.

(Note: I don't have compiler handy. Sorry for minor compiler errors.)

jason
  • 236,483
  • 35
  • 423
  • 525
  • Don't sweat any errors, this should get me started. I appreciate the help! (I'd vote you up but I don't have enough reputation.) I'll see if this works sometime today and report back. – MyOtherCarIsEpona Sep 13 '11 at 14:22
  • Another question - addressBytes.Zip - I'm having trouble finding that class. Would you happen to know which library it's in? – MyOtherCarIsEpona Sep 13 '11 at 14:32
  • My initial implementation would have done this; but I realised that `{192.168.0.0, 255.255.255.0}` is not the same as `{192.168.0.0, 255.255.0.0}` - according to this implementation it is. – Jonathan Dickinson Sep 13 '11 at 14:33
  • @MyOtherCarIsEpona: It's in .NET 4.0; be sure that you are using `System.Linq`. – jason Sep 13 '11 at 14:36
1

You don't need the list of subnets - they can be inferred. You can do it with some bitwise math:

// Key is {Subnet, Subnet Mask}.
// Value is list of IPs in that range.
public static IDictionary<Tuple<IPAddress, IPAddress>, ISet<IPAddress>> AllocateToSubnets(IEnumerable<Tuple<IPAddress, IPAddress>> ips)
{
    var dic = new Dictionary<Tuple<IPAddress, IPAddress>, ISet<IPAddress>>();
    foreach (var item in ips)
    {
        var subnet = Tuple.Create(GetSubnetAddress(item.Item1, item.Item2), item.Item2);

        ISet<IPAddress> set;
        if (!dic.TryGetValue(subnet, out set))
            dic.Add(subnet, set = new HashSet<IPAddress>());
        if (!set.Add(item.Item1))
        {
            // Throw an error if you are looking for duplicates.
        }
    }

    return dic;
}

private static IPAddress GetSubnetAddress(IPAddress address, IPAddress mask)
{
    var b1 = address.GetAddressBytes();
    var b2 = mask.GetAddressBytes();
    if (b1.Length != b2.Length)
        throw new InvalidOperationException("IPAddress and its mask do not share the same family.");
    for (var i = 0; i < b1.Length; i++)
        b1[i] &= b2[i];
    return new IPAddress(b1);
}

Test code:

var ips = new[]
    {
        Tuple.Create(IPAddress.Parse("192.168.0.1"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.1"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.2"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.10"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("192.168.0.10"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("10.0.0.1"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.1"), IPAddress.Parse("255.255.0.0")),
        Tuple.Create(IPAddress.Parse("10.0.0.2"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.10"), IPAddress.Parse("255.255.255.0")),
        Tuple.Create(IPAddress.Parse("10.1.0.10"), IPAddress.Parse("255.255.0.0")),
    };

var subnets = AllocateToSubnets(ips);
Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
0

This depends on which for the definition of your subnets has. If it's an IP address range you can use: Easiest way to check ip address against a range of values using c#

If it's a mask you can use ip&(~mask) to get the lower and ip|(~mask) to get the range and proceed as above.

If the subnet is given with the number of bits the netmask has you can calculate: mask=~(UInt32.MaxValue>>n) and use proceed as above.

If you want to find out the net mask for a given IP, then I have no clue. No idea about IPv6 either.

Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262