LoginSignup
3
1

More than 3 years have passed since last update.

IP v4 の address 範囲から CIDR を計算する

Last updated at Posted at 2019-09-10

Overview

これ の python3 と javascript 版。

javascript でブラウザで動かせるやつが欲しかったのだけど、意外と手こずったので、一度 python で書いてから js で書き直した。

検証はこの辺で。

python3 版

netaddr っていうお手本があるので、これを使うと楽。
ちなみに netaddr.iprange_to_cidrs っていうそのまんまな実装がすでにあるけど、今回は手書きしたかったので使いません。

a.py
import netaddr
import math

def iprange2cidr(start, end):
    iMask = lambda s: (2**32 - 2**(32-s))

    while end >= start:
        max_size = 32
        while max_size > 0:
            assert 0 < max_size <= 32
            mask = iMask(max_size - 1)
            if (start & mask) != start:
                break
            max_size -= 1

        x = math.log(end - start + 1) / math.log(2)
        max_diff = math.floor(32 - math.floor(x))

        if max_size < max_diff:
            max_size = max_diff

        yield "{}/{}".format(netaddr.IPAddress(start), max_size)
        start += 2**(32-max_size)


if __name__ == "__main__":
    expect = [
        "192.0.2.46/31",
        "192.0.2.48/28",
        "192.0.2.64/26",
        "192.0.2.128/25",
        "192.0.3.0/24",
        "192.0.4.0/22",
        "192.0.8.0/21",
        "192.0.16.0/20",
        "192.0.32.0/24",
        "192.0.33.0/29",
        "192.0.33.8/30",
        "192.0.33.12/31",
        "192.0.33.14/32",
    ]

    a1 = netaddr.IPAddress('192.0.2.46')
    b1 = netaddr.IPAddress('192.0.33.14')

    actual = list(iprange2cidr(int(a1), int(b1)))
    print(actual)
    assert actual == expect
$ python a.py
['192.0.2.46/31', '192.0.2.48/28', '192.0.2.64/26', '192.0.2.128/25', '192.0.3.0/24', '192.0.4.0/22', '192.0.8.0/21', '192.0.16.0/20', '192.0.32.0/24', '192.0.33.0/29', '192.0.33.8/30', '192.0.33.12/31', '192.0.33.14/32']

javascript 版

unsigned が明示できないのが罠でちょっとハマった。

a.js
class IPv4 {
  static OctetsToInt(octets) {
    const ret = (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3];
    return ret >>> 0; // cast to unsigned
  }

  static IntToOctets(i) {
    return [
      (i >>> 24) & 0xFF,
      (i >>> 16) & 0xFF,
      (i >>> 8)  & 0xFF,
      (i >>> 0)  & 0xFF,
    ];
  }

  static IntToString(i) {
    return this.OctetsToString(this.IntToOctets(i));
  }

  static StringToOctets(s) {
    return s.split(".").map((x) => parseInt(x, 10))
  }

  static OctetsToString(octets) {
    return octets.join(".")
  }

  static IntToBits(i) {
    const mask = [...Array(32).keys()].reverse().map((x) => 1<<x);
    const bits = mask.map((x) => i & x).map((x) => x != 0).map(Number);
    return bits.join("");
  }
}

function iprange2cidr(start, end) {
  const ret = [];
  const iMask = function(s) {
    return (2**32 - 2**(32-s));
  }

  while (end >= start) {
      let maxSize = 32;
      while (maxSize > 0) {
          const mask = iMask(maxSize-1);
          const maskBase = (start & mask)>>>0;  // cast to unsigned
          if (maskBase != start) {
              break;
          }
          maxSize -= 1;
      }

      const x = Math.log(end - start + 1) / Math.log(2);
      const maxDiff = Math.floor(32 - Math.floor(x));

      if (maxSize < maxDiff) {
          maxSize = maxDiff;
      }

      ret.push([IPv4.IntToString(start), maxSize].join("/"));
      start += 2**(32-maxSize);
  }

  return ret;
}

a1 = '192.0.2.46';
b1 = '192.0.33.14';

a2 = IPv4.OctetsToInt(IPv4.StringToOctets(a1));
b2 = IPv4.OctetsToInt(IPv4.StringToOctets(b1));

c = iprange2cidr(a2, b2);
console.log(c);
$ node a.js
[
  '192.0.2.46/31',  '192.0.2.48/28',
  '192.0.2.64/26',  '192.0.2.128/25',
  '192.0.3.0/24',   '192.0.4.0/22',
  '192.0.8.0/21',   '192.0.16.0/20',
  '192.0.32.0/24',  '192.0.33.0/29',
  '192.0.33.8/30',  '192.0.33.12/31',
  '192.0.33.14/32'
]

用途

CIDR計算がいちいちめんどくさいのでchrome拡張作りたかった時とか。

おわり。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1