Subnetting Functions to use in Excel

'============================================================================================
' Module: SubNetFunctions
' Author: Tom Willett
' Date: 9/6/2010
' Description:  This is a collection of subneting functions using cidr subnetting in VBA.
' The subnet functions are a cludge because vba does not have an unsigned long.
' All of the functions therefore, use the ascii representation of the binary numbers to do their calculations.
' The functions available are:
' broadcastIP -- given a subnet in cidr format returns the broadcastIP in dotted format
' cidr2mask -- given a cidr returns the equivalent mask
' highestIP -- given a subnet in cidr format returns the highest usable address in dotted format
' lowestIP -- given a subnet in cidr format returns the first usable address in dotted format
' subnetID -- given a subnet in cidr format returns the subnet id in dotted format
' ------ helper functions ----------
' AbinAnd -- binary ands two 8bit ascii binary numbers and returns the ascii binary result
' Bin2Dec -- given a binary number as a string returns the decimal equivalent as a string
' Dec2Bin -- given a decimal number as a string returns the binary equivalent as a string
' ip2long -- given an ip in dotted format returns the long equivalent (actually double)
' long2ip -- given a in represented as a long (actually double) returns the ip in dotted format
'=============================================================================================
Option Explicit

'======================================================
' Bin2Dec
'
' Parameter BinNum -- binary number as a string
' Return Decimal equivalent as a string
'======================================================
Public Function Bin2Dec(ByVal BinNum As String) As String
   Dim i As Integer
   Dim DecNum As Long
   
   On Error GoTo ErrorHandler
   
'  Loop thru BinString
   For i = Len(BinNum) To 1 Step -1
'     Check the string for invalid characters
      If Asc(Mid(BinNum, i, 1)) < 48 Or _
         Asc(Mid(BinNum, i, 1)) > 49 Then
         DecNum = ""
         Err.Raise 1002, "BinToDec", "Invalid Input"
      End If
'     If bit is 1 then raise 2^LoopCount and add it to DecNum
      If Mid(BinNum, i, 1) And 1 Then
         DecNum = DecNum + 2 ^ (Len(BinNum) - i)
      End If
   Next i
'  Return DecNum as a String
   Bin2Dec = CStr(DecNum)
ErrorHandler:
End Function

'==============================================================
' Dec2Bin
'
' Parameter DecNum -- decimal number as a string
' Return -- Binary equivalent of DecNum as a string
'===============================================================
Public Function Dec2Bin(ByVal DecNum As String) As String
   Dim BinNum As String
   Dim lDecNum As Long
   Dim i As Integer
   
   On Error GoTo ErrorHandler
   
'  Check the string for invalid characters
   For i = 1 To Len(DecNum)
      If Asc(Mid(DecNum, i, 1)) < 48 Or _
         Asc(Mid(DecNum, i, 1)) > 57 Then
         BinNum = ""
         Err.Raise 1010, "DecToBin", "Invalid Input"
      End If
   Next i
   
   i = 0
   lDecNum = Val(DecNum)
   
   Do
      If lDecNum And 2 ^ i Then
         BinNum = "1" & BinNum
      Else
         BinNum = "0" & BinNum
      End If
      i = i + 1
   Loop Until 2 ^ i > lDecNum
'  Return BinNum as a String
   Dec2Bin = BinNum
ErrorHandler:
End Function

'===============================================
' AbinAnd
'
' Parameters Bin1 and Bin2 are binary numbers represented as strings
' Return A binary number as a string which is the result of doing a binary and of Bin1 and Bin2
'===============================================
Public Function AbinAnd(ByVal bin1 As String, ByVal bin2 As String) As String
    '  This function takes two binary numbers as strings and returns the string representration of the two anded together
    Dim x As Byte
    Dim nBin As String
    
    nBin = String(8, "0")
    bin1 = Right(nBin & bin1, 8)
    bin2 = Right(nBin & bin2, 8)
    For x = 1 To 8
        If Mid(bin1, x, 1) = "1" And Mid(bin2, x, 1) = "1" Then
            Mid(nBin, x, 1) = "1"
        End If
    Next
    AbinAnd = nBin
End Function

'===========================================================
' cidr2mask
'
' Parameter -- cidr as a string -- decimal from 1 to 31
' Return the mask equivalent of the cidr in dotted format
'===========================================================

Public Function cidr2mask(cidr As String) As String
'converts the cidr bits to the subnet mask
    Dim BuildBin As String
    Dim octet(3) As String
    Dim x, y As Byte
    Dim cCtr As Byte
    
    cCtr = CByte(cidr)
    For x = 0 To 3
        BuildBin = ""
        For y = 1 To 8
            If cCtr > 0 Then
                BuildBin = BuildBin & "1"
            Else
                BuildBin = BuildBin & "0"
            End If
            If cCtr > 0 Then
                cCtr = cCtr - 1
            End If
        Next
        octet(x) = Bin2Dec(BuildBin)
    Next
    
    cidr2mask = Join(octet, ".")
End Function

'====================================================
' subnetID
'
'Given a subnet address in cidr format return the subnetID in dotted format
'====================================================
Public Function subnetID(subnet As String) As String
    Dim BuildBin As String
    Dim Parts As Variant
    Dim octet As Variant
    Dim Mask(3) As String
    Dim x, y As Byte
    Dim cCtr As Byte
    
    'split address and cidr
    On Error Resume Next
    Parts = Split(subnet, "/")
    octet = Split(Parts(0), ".")
    cCtr = CByte(Parts(1))
    'load mask octets
    For x = 0 To 3
        BuildBin = ""
        For y = 1 To 8
            If cCtr > 0 Then
                BuildBin = BuildBin & "1"
            Else
                BuildBin = BuildBin & "0"
            End If
            If cCtr > 0 Then
                cCtr = cCtr - 1
            End If
        Next
        Mask(x) = BuildBin
    Next
    'convert ip octets to binary string
    For x = 0 To 3
        octet(x) = Dec2Bin(octet(x))
    Next
    
    'AND Binary Expressions.

    For x = 0 To 3
        octet(x) = Bin2Dec(AbinAnd(Mask(x), octet(x)))
    Next

    subnetID = Join(octet, ".")
End Function

'=======================================================
' broadcaseIP
'
'given a subnet in cidr format return the broadcast address in dotted format
'=======================================================
Public Function broadcastIP(subnet As String) As String
    Dim BuildBin As String
    Dim Parts As Variant
    Dim octet As Variant
    Dim Mask(3) As String
    Dim IPBin As String
    Dim x, y As Byte
    Dim cCtr As Byte
    
    'split address and cidr
    On Error Resume Next
    Parts = Split(subnet, "/")
    octet = Split(Parts(0), ".")
    cCtr = CByte(Parts(1))
    'convert ip octets to binary string
    For x = 0 To 3
        octet(x) = Right("00000000" & Dec2Bin(octet(x)), 8)
    Next

    'Create Full IP as Binary
    IPBin = Join(octet, "")
    
    BuildBin = ""
    For x = 1 To 32
        If x <= cCtr Then
            BuildBin = BuildBin & Mid(IPBin, x, 1)
        Else
            BuildBin = BuildBin & "1"
        End If
    Next

    Mask(0) = Bin2Dec(Mid(BuildBin, 1, 8))
    Mask(1) = Bin2Dec(Mid(BuildBin, 9, 8))
    Mask(2) = Bin2Dec(Mid(BuildBin, 17, 8))
    Mask(3) = Bin2Dec(Mid(BuildBin, 25, 8))
    broadcastIP = Join(Mask, ".")
End Function

'==================================================
' lowestIP
'
' Parameter subnet in cidr format as a string
' Return the lowest usable ip in a cidr range
'==================================================

Public Function lowestIP(ByVal subnet As String) As String
    Dim BuildBin As String
    Dim Parts As Variant
    Dim octet As Variant
    Dim Mask(3) As String
    Dim x, y As Byte
    Dim cCtr As Byte
    
    'split address and cidr
    Parts = Split(subnet, "/")
    octet = Split(Parts(0), ".")
    cCtr = CByte(Parts(1))
    'load mask octets
    For x = 0 To 3
        BuildBin = ""
        For y = 1 To 8
            If cCtr > 0 Then
                BuildBin = BuildBin & "1"
            Else
                BuildBin = BuildBin & "0"
            End If
            If cCtr > 0 Then
                cCtr = cCtr - 1
            End If
        Next
        Mask(x) = BuildBin
    Next
    'convert ip octets to binary string
    For x = 0 To 3
        octet(x) = Dec2Bin(octet(x))
    Next

    'AND Binary Expressions.
    'convert octets to masked octets
    For x = 0 To 3
        octet(x) = Bin2Dec(AbinAnd(Mask(x), octet(x)))
    Next

    BuildBin = Right("00000000" & Dec2Bin(octet(3)), 8)
    Mid(BuildBin, 8, 1) = "1"
    octet(3) = Bin2Dec(BuildBin)
        
    lowestIP = Join(octet, ".")

End Function

'==============================================================
' highestIP
'
' Parameter subnet in cidr format as a string
' Return highest usable IP for given cidr
'==============================================================
Public Function highestIP(subnet As String) As String
    Dim BuildBin As String
    Dim Parts As Variant
    Dim octet As Variant
    Dim Mask(3) As String
    Dim IPBin As String
    Dim x, y As Byte
    Dim cCtr As Byte
    
    'split address and cidr
    Parts = Split(subnet, "/")
    octet = Split(Parts(0), ".")
    cCtr = CByte(Parts(1))
    'load mask octets
    For x = 0 To 3
        BuildBin = ""
        For y = 1 To 8
            If cCtr > 0 Then
                BuildBin = BuildBin & "1"
            Else
                BuildBin = BuildBin & "0"
            End If
            If cCtr > 0 Then
                cCtr = cCtr - 1
            End If
        Next
        Mask(x) = BuildBin
    Next
    'convert ip octets to binary string
    For x = 0 To 3
        octet(x) = Dec2Bin(octet(x))
    Next

    'AND Binary Expressions.
    'convert octets to masked octets
    For x = 0 To 3
        octet(x) = Right("00000000" & AbinAnd(Mask(x), octet(x)), 8)
    Next
    
    IPBin = Join(octet, "")
    BuildBin = ""

    cCtr = CByte(Parts(1))
    For x = 1 To 32
        If x &lf;= cCtr Then
            BuildBin = BuildBin & Mid(IPBin, x, 1)
        Else
            If x = 32 Then
                BuildBin = BuildBin & "0"
            Else
                BuildBin = BuildBin & "1"
            End If
        End If
    Next
    
    Mask(0) = Bin2Dec(Mid(BuildBin, 1, 8))
    Mask(1) = Bin2Dec(Mid(BuildBin, 9, 8))
    Mask(2) = Bin2Dec(Mid(BuildBin, 17, 8))
    Mask(3) = Bin2Dec(Mid(BuildBin, 25, 8))
    
    highestIP = Join(Mask, ".")
End Function

'==================================================
' IP2Long
'
' Parameter -- IP in dotted format as a string
' Return - decimal equivalent as a double
' VBA does not have an unsigned long :(
'==================================================
Public Function IP2Long(ByVal IP As String) As Double
         Dim IPLong As Double
         Dim IPpart As Variant
         Dim IPbyte(4) As Double
         'IPpart(0).IPpart(1).IPpart(2).IPpart(3)

         IPpart = Split(IP, ".")
         Dim x As Byte
         For x = 0 To 3
             IPbyte(x) = CByte(IPpart(x))
         Next
         IPLong = ((IPbyte(0) * (256 ^ 3)) + (IPbyte(1) * (256 ^ 2)) + (IPbyte(2) * 256) + IPbyte(3))

         IP2Long = IPLong
End Function

'=====================================================
' Long2IP
'
' Parameter Decimal value of the IP as a double
' Return dotted representation of the IP as a string
'=====================================================

Public Function Long2IP(ByVal LongIP As Double) As String
         Dim ByteIP(4) As String
         Dim x As Byte
         Dim IP As String

         If LongIP < 4294967296# And LongIP >= 0 Then
             ByteIP(0) = Fix(LongIP / (256 ^ 3))
             ByteIP(1) = Fix(((LongIP - (ByteIP(0) * (256 ^ 3))) / (256 ^ 2)))
             ByteIP(2) = Fix(((LongIP - (ByteIP(0) * (256 ^ 3)) - (ByteIP(1) * (256 ^ 2))) / 256))
             ByteIP(3) = ((LongIP - (ByteIP(0) * (256 ^ 3)) - (ByteIP(1) * (256 ^ 2)) - (ByteIP(2) * 256)))
             IP = ByteIP(0) & "." & ByteIP(1) & "." & ByteIP(2) & "." & ByteIP(3)
             Long2IP = IP
         Else
             Long2IP = -1
         End If
  End Function