开发者

IP cidr match function

I need to find out, is ip belong to ip mask. For example:

ip = 192.168.0.1 mask = 192.168.0.1/24.

I found function that convert ip to mask:

inet_cidrtoaddr(in开发者_StackOverflow社区t cidr, struct in_addr *addr)
{
        int ocets;

        if (cidr < 0 || cidr > 32) {
                errno = EINVAL;
                return -1;
        }
        ocets = (cidr + 7) / 8;

        addr->s_addr = 0;
        if (ocets > 0) {
                memset(&addr->s_addr, 255, (size_t)ocets - 1);
                memset((unsigned char *)&addr->s_addr + (ocets - 1),
                       (256 - (1 << (32 - cidr) % 8)), 1);
        }

        return 0;
}

How can i compare ip and cidr range ?


So to put Olis answer in code:

// Check if 192.168.0.1 is inside 192.168.0.0/24
in_addr ip, net, netmask;
inet_aton("192.168.0.1", &ip);
inet_aton("192.168.0.0", &net);

He said:

inet_cidrtoaddr(24, &netmask);
bool is_inside = ((ip.s_addr & netmask.s_addr) == (net.s_addr & netmask.s_addr));

I prefer the addr4_match method though:

bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits) {
  if (bits == 0) {
    // C99 6.5.7 (3): u32 << 32 is undefined behaviour
    return true;
  }
  return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
}
bool is_inside = cidr_match(ip, net, 24);

I experimented with a bunch of different input: https://gist.github.com/duedal/b83303b4988a4afb2a75

If somebody finding this is wanting an IPv6 solution too:

bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits) {
#ifdef LINUX
  const uint32_t *a = address.s6_addr32;
  const uint32_t *n = network.s6_addr32;
#else
  const uint32_t *a = address.__u6_addr.__u6_addr32;
  const uint32_t *n = network.__u6_addr.__u6_addr32;
#endif
  int bits_whole, bits_incomplete;
  bits_whole = bits >> 5;         // number of whole u32
  bits_incomplete = bits & 0x1F;  // number of bits in incomplete u32
  if (bits_whole) {
    if (memcmp(a, n, bits_whole << 2)) {
      return false;
    }
  }
  if (bits_incomplete) {
    uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
    if ((a[bits_whole] ^ n[bits_whole]) & mask) {
      return false;
    }
  }
  return true;
}

Check if 2001:db8::ff00:42:8329 is present in 2001:db8/32. Beware inet_net_pton is very picky, it's 2001:db8/32 not 2001:db8::/32. However 2001:db8::/48 is perfectly valid (also known as 2001:db8:0/48).

in6_addr ip6, net6, net6_48;
memset(&net6, 0, sizeof(net6));
memset(&net6_48, 0, sizeof(net6_48)); 
assert(inet_pton(AF_INET6, "2001:db8::ff00:42:8329", &ip6));

int bits = inet_net_pton(AF_INET6, "2001:db8/32", &net6, sizeof(net6));
assert((bits != -1));  // assert that inet_net_pton understood us
bool is_inside6 = cidr6_match(ip6, net6, bits);

int bits_48 = inet_net_pton(AF_INET6, "2001:db8::/48", &net6_48, sizeof(net6_48));
assert((bits_48 == 48));
bool is_inside6_48 = cidr6_match(ip6, net6_48, bits_48);


If you have the IP address, the network address, and the netmask, then you can use a function like this:

bool
is_in_net (
        const struct in_addr*   addr,     /* host byte order */
        const struct in_addr*   netaddr,
        const struct in_addr*   netmask
        )
{
   if ((addr->s_addr & netmask->s_addr) == (netaddr->s_addr & netmask->s_addr))
      return true;
   return false;
}


This function computes a net-mask (e.g. something of the form 255.255.255.128). So to check whether a specified IP address falls within a specified sub-net, just apply the mask to the CIDR address, and to the IP address (you do this with bitwise AND). If the results are the same, then the IP address is valid.


Try this:

const std::uint32_t CIDR_PREFIXES[33] = {
    [0] = htonl(0),
    [1] = htonl(0x80000000),
    [2] = htonl(0xC0000000),
    [3] = htonl(0xE0000000),
    [4] = htonl(0xF0000000),
    [5] = htonl(0xF8000000),
    [6] = htonl(0xFC000000),
    [7] = htonl(0xFE000000),
    [8] = htonl(0xFF000000),
    [9] = htonl(0xFF800000),
    [10] = htonl(0xFFC00000),
    [11] = htonl(0xFFE00000),
    [12] = htonl(0xFFF00000),
    [13] = htonl(0xFFF80000),
    [14] = htonl(0xFFFC0000),
    [15] = htonl(0xFFFE0000),
    [16] = htonl(0xFFFF0000),
    [17] = htonl(0xFFFF8000),
    [18] = htonl(0xFFFFC000),
    [19] = htonl(0xFFFFE000),
    [20] = htonl(0xFFFFF000),
    [21] = htonl(0xFFFFF800),
    [22] = htonl(0xFFFFFC00),
    [23] = htonl(0xFFFFFE00),
    [24] = htonl(0xFFFFFF00),
    [25] = htonl(0xFFFFFF80),
    [26] = htonl(0xFFFFFFC0),
    [27] = htonl(0xFFFFFFE0),
    [28] = htonl(0xFFFFFFF0),
    [29] = htonl(0xFFFFFFF8),
    [30] = htonl(0xFFFFFFFC),
    [31] = htonl(0xFFFFFFFE),
    [32] = htonl(0xFFFFFFFF),
};

bool matchCIDR(const in_addr &network, const in_addr &addr, unsigned prefix)
{
    if (prefix > 32)
        return false;

    std::uint32_t cidr = CIDR_PREFIXES[prefix];
    std::uint32_t hNetwork = network.s_addr;
    std::uint32_t hAddr = addr.s_addr;

    return hNetwork == (hAddr & cidr);
}

In C++ though, should be quite easy to make it C

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜