0

I am trying to convert dotted IP to integer using htonl(inet_addr(IP_str)).

The problem is the function returns negative results for IP address of class B and C.

here is some of my code (simplified):

int main() {
const char IP1[] = "10.0.0.0";  //Class A
const char IP2[] = "20.0.0.0";  //Class A    
const char IP3[] = "126.0.0.0";  //Class A
const char IP4[] = "128.0.0.0";  //Class B
const char IP5[] = "160.0.0.0";  //Class B
const char IP6[] = "190.0.0.0";  //Class B
const char IP7[] = "193.0.0.0";  //Class C
const char IP8[] = "200.0.0.0";  //Class C

uint32_t integer_ip;
integer_ip = htonl(inet_addr(IP1));
printf("IP1: %s ------> Integer: %d\n",IP1,integer_ip);
integer_ip = htonl(inet_addr(IP2));
printf("IP2: %s ------> Integer: %d\n",IP2,integer_ip);
integer_ip = htonl(inet_addr(IP3));
printf("IP3: %s ------> Integer: %d\n",IP3,integer_ip);   
integer_ip = htonl(inet_addr(IP4));
printf("IP4: %s ------> Integer: %d\n",IP4,integer_ip);
integer_ip = htonl(inet_addr(IP5));
printf("IP5: %s ------> Integer: %d\n",IP5,integer_ip);
integer_ip = htonl(inet_addr(IP6));
printf("IP6: %s ------> Integer: %d\n",IP6,integer_ip);
integer_ip = htonl(inet_addr(IP7));
printf("IP7: %s ------> Integer: %d\n",IP7,integer_ip);
integer_ip = htonl(inet_addr(IP8));
printf("IP8: %s ------> Integer: %d\n",IP8,integer_ip);
return 0;
}

and The resuls:

IP1: 10.0.0.0 ------> Integer: 167772160   
IP2: 20.0.0.0 ------> Integer: 335544320
IP3: 126.0.0.0 ------> Integer: 2113929216
IP4: 128.0.0.0 ------> Integer: -2147483648
IP5: 160.0.0.0 ------> Integer: -1610612736
IP6: 190.0.0.0 ------> Integer: -1107296256
IP7: 193.0.0.0 ------> Integer: -1056964608
IP8: 200.0.0.0 ------> Integer: -939524096

I tested The results on some online tools like this one.

Consider this line of output :

IP5: 160.0.0.0 ------> Integer: -1610612736

-1610612736 is equal to 160.0.0.0 whiles 1610612736 is equal to 96.0.0.0.

The most important requirement for me is speed. I don't want to check each IP if it is in class A or not.

How do i resolve this problem?

Thanks?

Rezaeimh7
  • 1,467
  • 2
  • 23
  • 40
  • 7
    Don't print the `uint32_t` as an signed integer then. – tkausl Feb 22 '17 at 14:08
  • 1
    _"because you can use the function in your C++ code"_ That's not a valid reason to use the [tag:c++] tag for your question. – πάντα ῥεῖ Feb 22 '17 at 14:12
  • Whoever told you about "class A, B, C" networks has knowledge so out of date that it's older than most of his students (many assumptions here, but they are probably right). CIDR replaced network classes 24 years ago. – Art Feb 22 '17 at 14:25

4 Answers4

4

Your code invokes undefined behaviour for printing an unsigned integer as a signed.

To print the stdint.h types, see inttypes.h. Use the PRIu32 macro to print a uint32_t:

uint32_t u = 42;
printf("Value: %" PRIu32 "\n", u);

The standard %u conversion type specificer is not guaranteed to be correct, if uint32_t is not typedef unsigned int uint32_t;. For example with 32 bit long and int, it could be typedefed to unsigned of either.

For hex, use PRIx32 or PRIX32.

Details:

The Macros map to a string literal with the conversion specifier appropriate for your platform, which can be "u". Notice the % must be given in the peceeding part. The standard C feature of concatenating adjscent string literals will generate the final format-string. There will be no run-time overhead.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
3

The only thing wrong is your format strings. Since those are unsigned numbers, you should be using %u instead of %d, assuming uint32_t maps to unsigned int on your compiler (which it probably does unless you're targeting an 8 or 16 bit processor).

If you want to ensure that your code is portable to systems where the int type is not 32 bits wide, you'll need to use the PRIu32 macro (or another PRI*32 macro, such as PRIx32 for hexadecimal) defined in inttypes.h

You use the macro in place of the format specifier like this:

printf("IP1: %s ------> Integer: %" PRIu32 "\n",IP1,integer_ip);
Eric Finn
  • 8,629
  • 3
  • 33
  • 42
3

As others have mentioned, the %d format specifier is for printing a signed int. The value you are passing is unsigned. Using the wrong format specifier invokes undefined behavior.

You need to use the %u format specifier to print the values as unsigned.

Better yet, use the %x format specifier instead to print the values as unsigned hexadecimal. That way, every pair of hex characters corresponds to a byte of the IP address, making it easier to read.

EDIT:

The C standard however doesn't guarantee that an int is at least 32 bits. You need to use the PRIu32 or PRIx32 macros to get the proper format specifier for a uint32_t:

printf("%" PRIx32 "\n", integer_ip);
dbush
  • 205,898
  • 23
  • 218
  • 273
1

htonl returns a 32-bit unsigned integer.

The format specifier %d you use to print the values is intended for signed integer values.

Use a format specifier that fits the data type (%u) and the values will be displayed as positive unsigned numbers.

tofro
  • 5,640
  • 14
  • 31
  • There is no guarantee `%u` or `%x` are correct either. – too honest for this site Feb 22 '17 at 14:21
  • There is a big chance that %u (or %x) is much more correct than %d. And, as the question is tagged Linux, there is a very high chance of being correct on non-exotic Linuxes – tofro Feb 23 '17 at 09:30
  • There is no guarantee `uint32_t` is an `unsigned int`. could be `unsigned long` as well on 32 bit Linux. While they would have the same representation, it still was UB according to the standard. Why not use the correct way? Feel free to state the problem the correct way would not solve. – too honest for this site Feb 23 '17 at 13:11