Skip to content

Commit 116be88

Browse files
committed
Added CidrNotation for IP address breakdown
1 parent d2d2e9f commit 116be88

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {expect, test} from 'vitest'
2+
import {getNetworkInfo} from "../../utils/CidrNotation.js";
3+
4+
test("Test getNetworkInfo IPV4", () => {
5+
const networkInfo = getNetworkInfo("255.0.0.8", 16);
6+
expect(networkInfo).toEqual({
7+
broadcastAddress: "255.0.255.255",
8+
capacity: 65536,
9+
ipv4: true,
10+
networkAddress: "255.0.0.0",
11+
prefix: 16,
12+
});
13+
});
14+
15+
test("Test getNetworkInfo IPV6", () => {
16+
const networkInfo = getNetworkInfo("2001:db8::1", 64);
17+
expect(networkInfo).toEqual({
18+
broadcastAddress: "2001:db8::ffff:ffff:ffff:ffff",
19+
capacity: 18446744073709552000,
20+
ipv4: false,
21+
networkAddress: "2001:db8:",
22+
prefix: 64,
23+
});
24+
});

client/src/utils/CidrNotation.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
const ipToInt = (ip) => {
2+
return ip.split('.').reduce((int, octet) => {
3+
return (int << 8) + parseInt(octet, 10);
4+
}, 0) >>> 0;
5+
};
6+
7+
const intToIp = (int) => {
8+
return [
9+
(int >>> 24) & 0xFF,
10+
(int >>> 16) & 0xFF,
11+
(int >>> 8) & 0xFF,
12+
int & 0xFF
13+
].join('.');
14+
};
15+
16+
const ipv6ToSegments = (ip) => {
17+
// Expand :: shorthand
18+
let segments = ip.split(':');
19+
const emptyIndex = segments.indexOf('');
20+
21+
if (emptyIndex !== -1) {
22+
const before = segments.slice(0, emptyIndex).filter(s => s !== '');
23+
const after = segments.slice(emptyIndex + 1).filter(s => s !== '');
24+
const missing = 8 - before.length - after.length;
25+
segments = [...before, ...Array(missing).fill('0'), ...after];
26+
}
27+
28+
return segments.map(s => parseInt(s || '0', 16));
29+
};
30+
31+
const segmentsToIpv6 = (segments) => {
32+
// Convert to hex strings
33+
let hexSegments = segments.map(s => s.toString(16));
34+
35+
// Find longest sequence of zeros for compression
36+
let maxZeroStart = -1;
37+
let maxZeroLen = 0;
38+
let currentZeroStart = -1;
39+
let currentZeroLen = 0;
40+
41+
for (let i = 0; i < hexSegments.length; i++) {
42+
if (hexSegments[i] === '0') {
43+
if (currentZeroStart === -1) {
44+
currentZeroStart = i;
45+
currentZeroLen = 1;
46+
} else {
47+
currentZeroLen++;
48+
}
49+
} else {
50+
if (currentZeroLen > maxZeroLen) {
51+
maxZeroStart = currentZeroStart;
52+
maxZeroLen = currentZeroLen;
53+
}
54+
currentZeroStart = -1;
55+
currentZeroLen = 0;
56+
}
57+
}
58+
59+
if (currentZeroLen > maxZeroLen) {
60+
maxZeroStart = currentZeroStart;
61+
maxZeroLen = currentZeroLen;
62+
}
63+
64+
// Compress if we found a sequence
65+
if (maxZeroLen > 1) {
66+
const before = hexSegments.slice(0, maxZeroStart);
67+
const after = hexSegments.slice(maxZeroStart + maxZeroLen);
68+
return [...before, '', ...after].join(':').replace(/:{2,}/, '::');
69+
}
70+
71+
return hexSegments.join(':');
72+
};
73+
74+
export const getNetworkInfo = (ipAddress, networkPrefix) => {
75+
// Detect IP version
76+
const isIPv6 = ipAddress.includes(':');
77+
78+
if (isIPv6) {
79+
// IPv6 processing
80+
const segments = ipv6ToSegments(ipAddress);
81+
const prefixBits = networkPrefix;
82+
83+
// Create mask and apply it
84+
const networkSegments = segments.map((segment, index) => {
85+
const bitPosition = index * 16;
86+
const bitsInSegment = Math.max(0, Math.min(16, prefixBits - bitPosition));
87+
88+
if (bitsInSegment === 0) return 0;
89+
if (bitsInSegment === 16) return segment;
90+
91+
const mask = (~0 << (16 - bitsInSegment)) & 0xFFFF;
92+
return segment & mask;
93+
});
94+
95+
// Calculate broadcast (all host bits set to 1)
96+
const broadcastSegments = segments.map((segment, index) => {
97+
const bitPosition = index * 16;
98+
const bitsInSegment = Math.max(0, Math.min(16, prefixBits - bitPosition));
99+
100+
if (bitsInSegment === 0) return 0xFFFF;
101+
if (bitsInSegment === 16) return segment;
102+
103+
const mask = (~0 << (16 - bitsInSegment)) & 0xFFFF;
104+
const hostMask = ~mask & 0xFFFF;
105+
return (segment & mask) | hostMask;
106+
});
107+
108+
const networkAddress = segmentsToIpv6(networkSegments);
109+
const broadcastAddress = segmentsToIpv6(broadcastSegments);
110+
const capacity = Math.pow(2, 128 - prefixBits);
111+
112+
return {
113+
networkAddress: networkAddress,
114+
broadcastAddress: broadcastAddress,
115+
capacity: capacity,
116+
ipv4: false,
117+
prefix: prefixBits
118+
};
119+
} else {
120+
// IPv4 processing
121+
const mask = (~0 << (32 - networkPrefix)) >>> 0;
122+
const ipInt = ipToInt(ipAddress);
123+
const networkInt = (ipInt & mask) >>> 0;
124+
const networkAddress = intToIp(networkInt);
125+
const broadcastInt = (networkInt | ~mask) >>> 0;
126+
const broadcastAddress = intToIp(broadcastInt);
127+
const capacity = Math.pow(2, 32 - networkPrefix);
128+
129+
return {
130+
networkAddress: networkAddress,
131+
broadcastAddress: broadcastAddress,
132+
capacity: capacity,
133+
ipv4: true,
134+
prefix: networkPrefix
135+
};
136+
}
137+
}

0 commit comments

Comments
 (0)