简体   繁体   中英

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

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;
  }

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.

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.

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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM