Skip to content

Instantly share code, notes, and snippets.

@klopp
Last active October 9, 2020 04:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klopp/9f99e56698ce30ed005474cc3ba6a0ac to your computer and use it in GitHub Desktop.
Save klopp/9f99e56698ce30ed005474cc3ba6a0ac to your computer and use it in GitHub Desktop.
#include <arpa/inet.h>
#include <string>
struct cidr;
static bool cidr_match(const cidr& src, const cidr& net);
static bool addr2cidr(const char* addr, cidr& entry);
/*
src & net can be any of:
"IPv4"
"IPv6"
"IPv4/BITS"
"IPv6/BITS"
"IPv4/IPv4"
"IPv4/IPv6"
"IPv6/IPv4"
"IPv6/IPv6"
*/
bool ip_match(const char* src, const char* net)
{
cidr ip, dest;
return(addr2cidr(src, ip) && addr2cidr(net, dest) && cidr_match(ip, dest));
}
struct cidr
{
/* Common */
int type;
uint32_t mask;
union {
in_addr_t net4;
in6_addr net6;
} net;
/* IPv6 only */
int bits_incomplete;
int bits_whole;
};
static bool addr2cidr(const char* addr, cidr& entry)
{
entry.type = -1;
entry.mask = 0xFFFFFFFFu;
entry.bits_incomplete = 0;
entry.bits_whole = 0;
std::string src_ip(addr);
std::string::size_type slash = src_ip.find('/');
if(slash != std::string::npos) {
src_ip.erase(slash);
}
int nettype = src_ip.find(':') == std::string::npos ? AF_INET : AF_INET6;
int bits = 0;
if(slash != std::string::npos) {
if(strspn(src_ip.c_str() + slash, ":.")) {
/* no address type check, get longest buffer */
in6_addr buf;
bits = inet_net_pton(nettype, src_ip.c_str() + slash + 1, &buf, sizeof(buf));
}
else {
bits = std::atoi(src_ip.c_str() + slash + 1);
}
if(bits < 0) {
return false;
}
}
if(nettype == AF_INET) {
int rc = inet_pton(AF_INET, src_ip.c_str(), &entry.net.net4);
if(rc == 0) {
/* 0 is returned if does not contain a character string
representing a valid network address in the specified address family */
return false;
}
if(rc < 0) {
/* If does not contain a valid address family, -1 is returned and errno
is set to EAFNOSUPPORT */
return false;
}
if(bits) {
entry.mask = htonl(0xFFFFFFFFu << (32 - bits));
}
}
else {
int rc = inet_pton(AF_INET6, src_ip.c_str(), &entry.net.net6);
if(rc == 0) {
/* see abowe */
return false;
}
if(rc < 0) {
/* see abowe */
return false;
}
entry.bits_whole = bits >> 5;
entry.bits_incomplete = bits & 0x1F;
if(entry.bits_incomplete) {
entry.mask = htonl((0xFFFFFFFFu) << (32 - entry.bits_incomplete));
}
}
entry.type = nettype;
return true;
}
static bool cidr_match(const cidr& src, const cidr& net)
{
if(src.type != net.type) return false;
if(src.type == AF_INET) {
/* IPv4 */
return !((src.net.net4 ^ net.net.net4) & net.mask);
}
/* IPv6 */
#ifdef __linux__
const uint32_t* a = src.net.net6.s6_addr32;
const uint32_t* n = net.net.net6.s6_addr32;
#else
const uint32_t* a = src.net.net6.__u6_addr.__u6_addr32;
const uint32_t* n = net.net.net6.__u6_addr.__u6_addr32;
#endif
if(net.bits_whole) {
if(memcmp(a, n, net.bits_whole << 2)) {
return false;
}
}
if(net.bits_incomplete) {
if((a[net.bits_whole] ^ n[net.bits_whole]) & net.mask) {
return false;
}
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment