logo hsb.horse
← ブログ一覧に戻る

ブログ

TypeScriptでCIDR範囲のIPアドレス判定を実装する

CloudFront Functionなど外部モジュールが使えない環境向けに、CIDR範囲内のIPアドレス判定を自前実装。ビット演算を使ったクラス構文の実装を整理。

公開日:

CloudFront Functionのような外部モジュールを使いづらい環境で、CIDR範囲内のIPアドレス判定を自前実装する。

実装

export class CIDR {
#cidr: string;
#baseIp: number;
#subnet: number;
constructor(cidr: string) {
this.#cidr = cidr;
const [baseIp, mask] = cidr.split("/");
this.#baseIp = this.#ipToLong(baseIp);
this.#subnet = this.#cidrToSubnet(Number.parseInt(mask, 10));
}
get cidr() {
return this.#cidr;
}
isInCidrRange(ip: string): boolean {
const ipLong = this.#ipToLong(ip);
const subnet = this.#subnet;
return (ipLong & subnet) === (this.#baseIp & subnet);
}
getCidrRange(): string[] {
const subnet = this.#subnet;
const startIp = this.#baseIp & subnet;
const endIp = startIp + (~subnet >>> 0);
const ips: string[] = [];
for (let i = startIp; i <= endIp; i++) {
ips.push(this.#longToIp(i));
}
return ips;
}
#ipToLong(ip: string): number {
return (
ip
.split(".")
.reduce((acc, octet) => (acc << 8) + Number.parseInt(octet, 10), 0) >>>
0
);
}
#longToIp(long: number): string {
const max = 255;
return [
(long >>> 24) & max,
(long >>> 16) & max,
(long >>> 8) & max,
long & max,
].join(".");
}
#cidrToSubnet(cidr: number): number {
return (-1 << (32 - cidr)) >>> 0;
}
}

使い方

const cidr = new CIDR("192.168.1.0/24");
// IPアドレスが範囲内かチェック
console.log(cidr.isInCidrRange("192.168.1.100")); // true
console.log(cidr.isInCidrRange("192.168.2.1")); // false
// CIDR範囲内の全IPアドレスを取得
const ips = cidr.getCidrRange();
console.log(ips.length); // 256

ポイント

IPアドレスとlong値の相互変換

IPアドレス(例: 192.168.1.1)を32ビット整数に変換することで、ビット演算が可能になる。

CIDR判定

サブネットマスクとのAND演算で、IPアドレスがCIDR範囲内かを判定する。

(ipLong & subnet) === (baseIp & subnet)

範囲内の全IPを列挙

開始IPと終了IPを計算し、その間のすべてのIPアドレスを生成する。

まとめ

外部ライブラリが使えない環境でも、ビット演算を使えばCIDR判定を自前実装できる。

CloudFront Functionやその他の制約のある環境で、IP制御を行う際に活用できる。