2

I`m working on a project using TypeScript and have access to a key-value based storage. The requirement is to find data related to a single IP (match key).

Unfortunately the key is always a CIDR covering a lot of IP's (to save storage due to many records). During my tests I was unable to find the correct CIDR belonging to a specific IP.

Example data:

"103.21.244.0/24" - "data, lorem ipsum, etc"

Example IP to find:

"103.21.244.1"

I have tested several libraries like: ip-address, ip-num, ip-to-int, ipaddr.js and more, but I am unable to get the result I want.

Maybe I am just being dumb and do not understand the IP specification correctly, or maybe I am just misusing these libraries, please enlighten me.

Surely there has to be a way without calling external API's (like RIPE) and without having to store billions of IP's instead of their CIDR.

Essentially the requirement is quite simple: "find this KEY (in CIDR) by this IP (v4 or v6)".

Any help, advice, example solutions are highly appreciated.

Mecanik
  • 1,539
  • 1
  • 20
  • 50

1 Answers1

2

IP address can be converted to a single number. The a.b.c.d form is a just base-256 representation of a 32-bit number. Convert both the address and the subnet mask to integer. Then use "bitwise and" operation to apply the mask to the target IP addresses and compare with the network address.

function addrToNumber(addr)
{
  return addr.split(".").reduce((acc,cur,i)=> acc += (Number(cur) << ( (3-i) * 8) ) ,0)
}

function subnetMaskToNumber(mask)
{
  return (0xffffffff << (32 - Number(mask))) & 0xffffffff;
}

const cidr = "103.21.244.0/24";
const [networkAddr,subnetMask] = cidr.split("/");

const ipAddr = "103.21.244.1";

const match = (addrToNumber(ipAddr) & subnetMaskToNumber(subnetMask)) == addrToNumber(networkAddr)
console.log(match);

To generate all possible CIDR for an IP address:

function addrToNumber(addr)
{
  return addr.split(".").reduce((acc,cur,i)=> acc += (Number(cur) << ( (3-i) * 8) ) ,0)
}
function numberToAddr(num)
{
  return `${(num >> 24) & 0xff}.${(num >> 16) & 0xff}.${(num >> 8) & 0xff}.${num & 0xff}`
}

const ipAddr = "103.21.244.1";

const ipValue = addrToNumber(ipAddr);

let possibleCIDR = [...Array(33)].map((_,i)=>{
  let mask = i == 0 ? 0 : (0xffffffff << (32 - i)) >>> 0;
  return `${numberToAddr(ipValue & mask)}/${i}`;
});

console.log(possibleCIDR);

To generate possible CIDR for IPv6, zeros compression not handled.

const ipv6 = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
const groups = ipv6.split(":");
let possibleCIDR = [...Array(129)].map((_,i)=>{
  let groupIndex = Math.floor(i / 16);
  let mask = (i % 16) ? (0xffff << (16 - (i % 16))) & 0xffff : 0;
  return groups.map((value,j)=>{
    return (j < groupIndex ? value : j == groupIndex ? parseInt(value,16) & mask : 0).toString(16)
  }).join(":") + `/${i}`;
});
console.log(possibleCIDR);
Ricky Mo
  • 6,285
  • 1
  • 14
  • 30
  • I rushed to comment, even though this is great this does not resolve my issue. I need something to find all possible CIDR for 1 single IP, because my storage is key:value so I cannot search inside it just by key. Meaning, I need to know the IP's CIDR (or possible list of them) to try and find the correct one. – Mecanik Oct 20 '21 at 14:09
  • @NorbertBoros updated the answer – Ricky Mo Oct 21 '21 at 01:42
  • This is perfect... exactly what I needed! If it's not too much trouble, could you please show me something for IPv6 as well please? Same thing. – Mecanik Oct 21 '21 at 03:39
  • IPv6 consists of 128bit and won't fit in a single number. Need to divide it into smaller groups first to work with. – Ricky Mo Oct 21 '21 at 03:51
  • I can imagine... I only need the part where I can get all possible CIDR, because as I mentioned I will be doing a foreach and attempt to find the CIDR in my data set. Would really, really appreciate if you could help! – Mecanik Oct 21 '21 at 03:52
  • @NorbertBoros edited for ipv6, though zeros compression is not handled. You may find how to do the zeros compression in [this post](https://stackoverflow.com/questions/44432191/how-to-compress-ipv6-address-using-javascript) – Ricky Mo Oct 21 '21 at 04:10
  • @NorbertBoros the code was wrong, I edited again. Forgot to `parseInt` – Ricky Mo Oct 21 '21 at 04:20
  • This is great, thank you. I'm having quite some trouble with IPv6 though, as it generates me over 200 ranges for a single IP... don't know how to improve this. – Mecanik Oct 31 '21 at 08:23
  • You can see what I mean here: https://pastebin.com/mrrBTQfz, I also tried to use that compression but without success so far. – Mecanik Oct 31 '21 at 08:32
  • I get false on the first version for `"1.1.15.200","1.1.1.0/20"` where https://www.ipaddressguide.com/cidr tells me the range is 1.1.0.0 - 1.1.15.255 – mplungjan Jan 01 '23 at 20:10