IP cidr match function

时光怂恿深爱的人放手 提交于 2019-12-06 00:41:47

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

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

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!