Link to home
Start Free TrialLog in
Avatar of sambha03
sambha03

asked on

Ip address and subnet mask as string

I have twos strings, one holds a valid ip address and the other its subnet mask.
I need the network address ( AND of the two). How can I check that the subnet mask is valid ( all 1's followed by all 0's) and get the network number?
E.g.
IPstr="123.4.5.6"
subnetStr="255.255.0.0"
Should give:
"123.4.0.0" as string (network address)

To validate the subnet mask I can arithematically examine each bit in each component and use a flag to check if its all 1's followed by all 0. Is there an easier way? I am a little lost on how perform the bit wise AND operation on these.

ASKER CERTIFIED SOLUTION
Avatar of SilentRage
SilentRage

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of SilentRage
SilentRage

oh, I guess you want a description of what's up eh?

Well, inet_addr converts a string IP into a long number.  So I convert both mask and IP to long numbers so that I can do the bitwise 'And' on the entire address at once for efficiency.  The Result is the long form of the resulting address you want.  I must however convert it to string form.  I considered different methods and decided that one was both efficient, clean, and easy to understand all in one.

So I used CopyMemory to copy the 4 byte long value to a 4 byte array.  This way I can conveniently insert the bytes into their proper dotted IP positions.  btw, I just realized that you can use a join there too.

take this line

GetNetworkAddr = Addr(0) & "." & Addr(1) & "." & Addr(2) & "." & Addr(3)

replace with this line

GetNetworkAddr = Join(Addr, ".")

:)
Avatar of sambha03

ASKER

This looks great ..really simple too. I'll try it out. Thanks:-)
Any ideas on how to check if subnet mask is valid?
I know it wasnt a part of the original question but do have any ideas on how validate the ip address too? Looking at this makes me think that maybe I am planning things in a very complicated way. I am planing to use string manipulation to check each component of string for 0-255 range and check number of dots in the address. Is there a simpler way?
As for a more efficient means of checking IP validity, I gave you half the answer already.  :)  If inet_addr can't convert and IP address to proper form, it's not a valid IP.  :)  The only problem with that approach is that an IP address takes up the full possible range of values that can be stored in a long.  So the return error (-1 or the IP equivilent 255.255.255.255) may fool you into believing that the IP address is valid.  There's one other quirk about inet_addr.  It accepts partial IP addresses as well (127.0.0.0 = 127.0 = 127 etc).

So here's my recommendation on the most efficient way to verify an IP address.

If inet_addr(IP) <> -1 Or IP = "255.255.255.255" Then
    'valid address
    If Len(IP) = Len(replace(IP, ".", "")) + 3 then
        'verified that there are 3 periods
    End If
End If

probably a lot less code and a lot faster than what you're doing.  :)

as for validating if an IP is apart of the local network... I'll research how that is done and give you the code for that as well.
well, I got tired of messing with bad google results so I sat down and figured things out mathematically/logically.  Here's the fruits of my testing:

Private Declare Function inet_addr Lib "wsock32.dll" (ByVal cp As String) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Private Sub Form_Load()
Dim NetAddr As Long, TestIP1 As Long, TestIP2 As Long
    'returns: 192.168.1.192 in long form
    NetAddr = GetNetworkAddr("255.255.255.192", "192.168.1.195")

    TestIP1 = inet_addr("192.168.1.205")    'valid IP
    TestIP2 = inet_addr("192.168.1.123")    'invalid IP
    TestIP3 = inet_addr("127.0.0.1")        'another invalid IP

    Debug.Print LongToIP(NetAddr Or TestIP1) '192.168.1.205
    Debug.Print LongToIP(NetAddr Or TestIP2) '192.168.1.251
    Debug.Print LongToIP(NetAddr Or TestIP3) '255.168.1.193
End Sub
Private Function GetNetworkAddr(ByVal Mask As String, ByVal IP As String) As String
    GetNetworkAddr = inet_addr(Mask) And inet_addr(IP)
End Function
Private Function LongToIP(ByVal Address As Long) As String
Dim Addr(3) As Byte
    CopyMemory Addr(0), Address, 4
    LongToIP = Addr(0) & "." & Addr(1) & "." & Addr(2) & "." & Addr(3)
End Function

I figured that the NetAddr is what best represents both the mask and network, so I figured it was the key value to be used in validating other IP's.  Then I tested it against other IP's using AND - but I didn't see any useable patterns.  Then I tried OR and bingo!  If you bitwise or an IP against a network address, and the result is the SAME as the test IP, then it belongs on the network.  Otherwise, it does not.

You can try other IP values to make sure my rule fits all scenarios.  My hunch is that it does.
btw, notice that I used an uncommon (but valid!) subnet mask to be sure the trick worked - and not figure out some logic scheme that only worked with 0 ended masks.
This is really great :-) You already solved 95% of my problem. I wasnt refering to checking if Ip is in the network address but just checking that the subnet mask is valid ( all 1's followed by all 0's). E.g 255.0.255.255 is invalid mask (1's followed by 0's and again 1's). Other samples are:
255.128.0.0 : valid mask
255.128.0.1 : invalid mask
I appreciate your effort a lot. Thanks for the help. I have increased the points.
oooooooooooooooh, so that's what you were talking about with the 1's and 0's.  heh.  I have a bad habit of ignoring what I don't understand.

but anyway... validating whether a subnet mask is valid.  Well, I spent a long time trying to think of an efficient way, but I'm tired of thinking.  So here's a way:

Private Function ValidMask(ByVal NetMask As String) As Boolean
Dim Mask As Long, BitIndex As Long, Addr(3) As Byte, X As Long, Y As Long
    'This failed before, gotta make it specific
    If NetMask = "255.255.255.255" Then
        ValidMask = True
        Exit Function
    End If

    'It would have to be a valid IP to be a valid mask right?  :)
    If ValidIP(NetMask) Then
        Mask = inet_addr(NetMask)
        'do that array of bytes thing again
        CopyMemory Addr(0), Mask, 4

        'Do the greater than test, an IP segment will never be greater than a previous
        If Addr(0) >= Addr(1) And Addr(1) >= Addr(2) And Addr(2) >= Addr(3) Then
            For X = 0 To 3
                If ValidMask = True And Addr(X) > 0 Then
                    ValidMask = False
                    Exit Function
                ElseIf Addr(X) < 255 Then
                    'make sure the bits are sorted right
                    For Y = 8 To 2 Step -1
                        'If this bit is lower than the next bit - INVALID!!!
                        If (Addr(X) And (Y ^ 2)) < (Addr(X) And ((Y - 1) ^ 2)) Then
                            Exit Function
                        End If
                    Next
                    ValidMask = True
                End If
            Next
        End If
    End If
End Function
oh, forgot to include my ValidIP function.

Private Function ValidIP(ByVal IP As String) As Boolean
    If inet_addr(IP) <> -1 Or IP = "255.255.255.255" Then
       ValidIP = Len(IP) = Len(Replace(IP, ".", "")) + 3
    End If
End Function
Thanks I got it working.