giving something back after taking a lot :-)

Maik Koster at myITforum.com

Get Active Directory Site for IP Address

January 19, 2009

During the preparation of the SMS/SCCM Webservice for MDT/OSD Deployments I came across a problem to get the Active Directory Site for a given IP Address. There were a couple of examples available on the Internet but most of them just called the DsGetDcName API. As this is possible within the .Net Framework I personally try to avoid using a com API if using .Net.

Actually the .Net Framework 2.0 contains a namespace called "System.DirectoryServices.ActiveDirectory". So I thought it must be possible to get this information. The first thing we would need is a list of all Active Directory sites with their corresponding Subnets. How do get all sites? The Forest Object will do the trick. Forest.GetCurrentForest.Sites will return a list of all sites. Each site object will then return a list of all subnets. Now we only need to check if the given IP Address is part of a single subnet and if we have a positive match we have the name of the corresponding site. So a function to loop through all of them could look like this:

Public Shared Function GetSite(ByVal IPAddress As String) As String
    Dim Location As String = ""

    For Each site As ActiveDirectorySite In Forest.GetCurrentForest.Sites
        For Each subnet As ActiveDirectorySubnet In site.Subnets
            If IPInSubnet(IPAddress, subnet.Name) Then
                Location = site.Name
            End If
        Next
    Next

    Return Location
End Function

So far so good. Now we need to check if a single IP Address is within a subnet. One way could be to get the first and last IP Address of the subnet and then see if the IP Address can be found between them.

Public Shared Function IPInSubnet(ByVal IPAddress As String, ByVal subnet As String) As Boolean
    Dim Result As Boolean = False

    Dim SplittedSubnetName As String() = subnet.Split("/")
    If SplittedSubnetName.Length = 2 Then
        Dim IPAddressInDecimal As Long = IPToDecimal(IPAddress)
        Dim SubnetmaskBits As Integer = Integer.Parse(SplittedSubnetName(1))
        Dim NoOfAddresses As Integer = Convert.ToInt32(Math.Pow(2, (32 - SubnetmaskBits)) - 1)
        Dim LowIPAddress As Long = IPToDecimal(SplittedSubnetName(0))
        Dim HighIPAddress As Long = LowIPAddress + NoOfAddresses
        Dim TotalIPAddressCount As Long = (Convert.ToInt64(Math.Pow(2, 31))) - 1

        If LowIPAddress <= IPAddressInDecimal AndAlso IPAddressInDecimal <= HighIPAddress _
            AndAlso NoOfAddresses <= TotalIPAddressCount Then
            Result = True
        End If
    End If

    Return Result
End Function

To ease the calculations, we just convert the IP Address to a Long (64 Bit Integer) Value

Public Shared Function IPToDecimal(ByVal IPAddress As String) As Long
    Dim IPAddresses As String() = IPAddress.Split(".")

    Return Convert.ToInt64(((Int32.Parse(IPAddresses(0)) * Math.Pow(2, 24) + _
        (Int32.Parse(IPAddresses(1)) * Math.Pow(2, 16) + _
        (Int32.Parse(IPAddresses(2)) * Math.Pow(2, 8) + _
        (Int32.Parse(IPAddresses(3))))))))
End Function

So far so good. Now we could use this function within our webservice to get the ADSite for any IP Address. For sure only if we have the appropiate subnets configured :-)

Finally as a small goody. Have you ever tried to check all your AD Site subnets if they maybe overlap or collide with any other? Using the same functions we have just seen this task is quite easy. Add the following two functions to your class and it will return a list of all Site-Subnet combinations which overlap with another subnet.

Public Shared Function IPInSubnet(ByVal IPAddress As String, ByVal subnet As String) As Boolean
    Dim Result As Boolean = False

    Dim SplittedSubnetName As String() = subnet.Split("/")
    If SplittedSubnetName.Length = 2 Then
        Dim IPAddressInDecimal As Long = IPToDecimal(IPAddress)
        Dim SubnetmaskBits As Integer = Integer.Parse(SplittedSubnetName(1))
        Dim NoOfAddresses As Integer = Convert.ToInt32(Math.Pow(2, (32 - SubnetmaskBits)) - 1)
        Dim LowIPAddress As Long = IPToDecimal(SplittedSubnetName(0))
        Dim HighIPAddress As Long = LowIPAddress + NoOfAddresses
        Dim TotalIPAddressCount As Long = (Convert.ToInt64(Math.Pow(2, 31))) - 1

        If LowIPAddress <= IPAddressInDecimal AndAlso IPAddressInDecimal <= HighIPAddress _
            AndAlso NoOfAddresses <= TotalIPAddressCount Then
            Result = True
        End If
    End If

    Return Result
End Function

Public Shared Function SubnetOverlapsSubnet(ByVal Subnet1 As String, ByVal Subnet2 As String)
    Dim result As Boolean

    Dim splittedsubnetname1 As String() = Subnet1.Split("/")
    Dim splittedsubnetname2 As String() = Subnet2.Split("/")

    Dim SubnetmaskBits1 As Integer = Integer.Parse(splittedsubnetname1(1))
    Dim SubnetmaskBits2 As Integer = Integer.Parse(splittedsubnetname2(1))

    Dim NoOfAddresses1 As Integer = Convert.ToInt32(Math.Pow(2, (32 - SubnetmaskBits1)) - 1)
    Dim NoOfAddresses2 As Integer = Convert.ToInt32(Math.Pow(2, (32 - SubnetmaskBits2)) - 1)

    Dim LowIPAddress1 As Long = IPToDecimal(splittedsubnetname1(0))
    Dim LowIPAddress2 As Long = IPToDecimal(splittedsubnetname2(0))

    Dim HighIPAddress1 As Long = LowIPAddress1 + NoOfAddresses1
    Dim HighIPAddress2 As Long = LowIPAddress2 + NoOfAddresses2

    If (LowIPAddress1 < LowIPAddress2 AndAlso HighIPAddress1 < LowIPAddress2) _
        OrElse (LowIPAddress1 > HighIPAddress2 AndAlso HighIPAddress1 > HighIPAddress2) Then
        result = False
    Else
        result = True
    End If

    Return result
End Function


The subnet value needs to in the Format of 192.168.20.0/24 to get a proper result. You might wanna add code to parse 192.168.20.0/255.255.255.0 by yourself :-).

As always you can download the full source code.

Filed under:

Comments

  • No Comments