5

I have an IP like this: 12.12.12.12
I'm looping through different IP ranges (in 12.12.12.0/24 (example)) format, and trying to see if the IP is in the range.
I have tried various methods such as inet_addr and comparing but I can't seem to get it.
Is there an easy way to do this? I'm using Windows.

seth
  • 139
  • 3
  • 8
  • Related: http://stackoverflow.com/questions/1507579/how-can-i-determine-that-user-ip-address-is-in-accessible-range-or-not – Marc B Sep 26 '11 at 18:53
  • That's PHP, as you can see from the tags i'm using C++ – seth Sep 26 '11 at 18:54

2 Answers2

9

Just test whether:

(ip & netmask) == (range & netmask)

You can determine the netmask from the CIDR parameters range/netbits as follows:

uint32_t netmask = ~(~uint32_t(0) >> netbits);
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    a `ntohl` conversion could be required before doing this. – KillianDS Sep 26 '11 at 19:04
  • @KillianDS: I'm assuming that the parsing process yields an address in local byte order, but you're right, it is something to double-check, especially since `inet_addr` returns network byte order. – Ben Voigt Sep 26 '11 at 19:08
  • Do you see something wrong here? Seems to print for every single one: `int cidr = atoi(cdrbuf); unsigned long ulstart = ntohl(inet_addr(clip)); unsigned long ulcheck = ntohl(inet_addr("12.12.12.12")); unsigned long netmask = ~unsigned long(~0ULL >> cidr); if((ulcheck & netmask) == (cidr & netmask)) printf("%s\n", pch);` – seth Sep 26 '11 at 19:09
  • @seth: What you do get from `printf("%08x %08x %d\n", ulstart, ulcheck, cidr);`? But the mask is supposed to test `range` (or `ulstart` in your naming), not `cidr`. – Ben Voigt Sep 26 '11 at 19:17
  • Ben, many like this:00000224 0c0c0c0c 15 00000824 0c0c0c0c 13 00002024 0c0c0c0c 14 00802524 0c0c0c0c 17 00402624 0c0c0c0c 18 00202724 0c0c0c0c 19 00402724 0c0c0c0c 18 00002824 0c0c0c0c 14 00003424 0c0c0c0c 14 0000c024 0c0c0c0c 11 0000e024 0c0c0c0c 12 0000f024 0c0c0c0c 13 0000fc24 0c0c0c0c 15 – seth Sep 26 '11 at 19:21
  • @seth: For one thing, the byte order of `ulstart` is wrong, but that doesn't explain 100% matching. Print out netmask also (using `%08x`). – Ben Voigt Sep 26 '11 at 19:25
  • @seth: rats: The shift was shifting in ones, if 0ULL was more than 32 bits. Fixed my answer. Or use @bitmask's formula of `(~0U) << (32-cidr)` – Ben Voigt Sep 26 '11 at 19:27
  • `unsigned long ulstart = ntohl(inet_addr(clip)); unsigned long ulcheck = ntohl(inet_addr("12.12.12.12")); unsigned long netmask = ~(~unsigned long(0) >> netmask);` `if((ulcheck & netmask) == (ulstart & netmask)) printf("%08x %08x %08x %d\n", ulstart, ulcheck, netmask, cidr);` dfffe500 0c0c0c0c 00000000 24 dfffe600 0c0c0c0c 00000000 24 dfffe800 0c0c0c0c 00000000 22 dffff000 0c0c0c0c 00000000 24 dffff400 0c0c0c0c 00000000 22 dfffff00 0c0c0c0c 00000000 24 – seth Sep 26 '11 at 19:31
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3803/discussion-between-seth-and-ben-voigt) – seth Sep 26 '11 at 20:31
  • @seth: `netmask` should be calculated from `cidr`, not itself. But the final comparison involves `netmask`, not `cidr`. Should be `uint32_t netmask = ~(~uint32_t(0) >> cidr); if((ulcheck & netmask) == (ulstart & netmask))` – Ben Voigt Sep 26 '11 at 21:16
  • See http://ideone.com/ZuOr2 where the formula correctly produces a non-zero netmask. – Ben Voigt Sep 26 '11 at 21:21
  • 1
    `~uint32_t(0)` is a funny way of writing `0xffffffffu`. – caf Sep 27 '11 at 06:56
  • @caf: If by "funny", you meant "portable". What if `int` is 16-bits on the target platform? – Ben Voigt Sep 27 '11 at 14:06
  • 1
    @Ben Voigt: Then `0xffffffffu` will have type `unsigned long`, and still have the same *value*. Read up on the rules for constants. – caf Sep 27 '11 at 21:45
  • @caf: "The type of an integer literal is the first of the corresponding list in Table 6 in which its value can be represented." Ok, you're right, so why do you see people tagging `ULL` on the end of literals all the time? (apparently I use too many different languages, I tend to use patterns that work in all of them). Anyway, my version definitely IS portable. – Ben Voigt Sep 27 '11 at 21:52
  • @Ben Voigt: You might use `ULL` if you are going to do something like left-shift the constant, where promotion won't occur. In this case, either formulation is portable. – caf Sep 28 '11 at 01:11
2

Take the binary representation and zero out what is not matching your network mask.

Clarification: Let's say you have the IP a.b.c.d and want to match it to e.f.g.h/i then, you can throw the IP into one unsigned integer, uint32_t ip = a<<24 + b<<16 + c<<8 + d and do the same with uint32_t range = e<<24 + f<<16 + g<<8 + h. Now you can use your network mask: uint32_t mask = (~0u) << (32-i). Now, you can simply check if ip "is in" range by comparing them: ip & mask == range & mask.

bitmask
  • 32,434
  • 14
  • 99
  • 159