reza
reza

Reputation: 6358

netmask conversion to CIDR format in C++

I have to convert 2 DWORDs, IP address and a network mask to CDIR format... So I have 2 DWORDs corresponding 1.1.1.1 and 255.255.255.255 and I want to come up with string 1.1.1.1/32

Any thoughts on this?

Thanks

Upvotes: 3

Views: 13972

Answers (5)

David Jordan
David Jordan

Reputation: 11

This isn't completely portable, but is likely the fastest way of doing this if your program is running on a chip that supports the Intel instruction set. (And a similar approach may work for other architectures). The Intel chips have an instruction called POPCNT, which returns the number of bits in a variable that are set to 1. Most compilers should provide an intrinsic to access this. For example, the Microsoft compilers give you __popcnt and GCC gives you __builtin_popcount. (With various flavors for different argument sizes).

Assuming a well-formed mask (as do all of the other solutions offered here), you can get the CIDR bit count with a single machine instruction.

inline uint32_t getBitCountFromIPv4Mask(uint32_t mask) {
    return __builtin_popcount(mask);    // presumes a well-formed mask.
}

You can do this with IPv6 masks as well. There's a 64-bit version of POPCNT, but since in6_addr won't give out address fragments in 64 bit chunks, it's probably best to not use it here.

uint32_t getBitCountFromIPv6Mask(const in6_addr &mask) {
    uint32_t bitCount = 0;

    for (uint32_t ii = 0; ii < 4; ii++) {
        bitCount += __builtin_popcount(mask.s6_addr32[ii]);
    }

    return bitCount;
}

Upvotes: 1

dngot_
dngot_

Reputation: 433

Simplest approach:

  static unsigned short toCidr(char* ipAddress)
  {
      unsigned short netmask_cidr;
      int ipbytes[4];

      netmask_cidr=0;
      sscanf(ipAddress, "%d.%d.%d.%d", &ipbytes[0], &ipbytes[1], &ipbytes[2], &ipbytes[3]);

      for (int i=0; i<4; i++)
      {
          switch(ipbytes[i])
          {
              case 0x80:
                  netmask_cidr+=1;
                  break;

              case 0xC0:
                  netmask_cidr+=2;
                  break;

              case 0xE0:
                  netmask_cidr+=3;
                  break;

              case 0xF0:
                  netmask_cidr+=4;
                  break;

              case 0xF8:
                  netmask_cidr+=5;
                  break;

              case 0xFC:
                  netmask_cidr+=6;
                  break;

              case 0xFE:
                  netmask_cidr+=7;
                  break;

              case 0xFF:
                  netmask_cidr+=8;
                  break;

              default:
                  return netmask_cidr;
                  break;
          }
      }

      return netmask_cidr;
  }

Upvotes: 7

St&#233;phane
St&#233;phane

Reputation: 20370

Not efficient if you have a large amount of these to do, as it goes through the bits one at a time. But very straightforward way to count each bit in the netmask:

int cidr = 0;
while ( netmask )
{
    cidr += ( netmask & 0x01 );
    netmask >>= 1;
}

Then combine the IP address with this CIDR value.

Upvotes: 3

e.dan
e.dan

Reputation: 7507

Since there are a small and fixed number of valid netmasks (32, to be exact), the fastest way is probably to just build a map of masks to prefix length once at init time, and the conversion is just a lookup in the map.

Upvotes: 4

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272687

The prefix-length is equal to the number of (leading) ones in the binary representation of the subnet mask. So you just need to count the number of (leading) ones.

Upvotes: 3

Related Questions