Java〜サブネットマスクの表記変換(CIDR<->IPv4)〜
サブネットマスクを書くとき、「255.255.255.0」のようにIPアドレスのように書く方式と、
「/24」のようにCIDR(サイダー)と呼ばれる表記方法を用いる場合がありますよね。
今回はこのサブネットの、IPアドレス表記とCIDR表記の相互変換のコードを紹介します。
自分で書かなくても、どこかにありそうな気がするんだけど、あまり良さげなのが見つからなかったんですよね。
ここにCIDRからIP形式に変換するコードはあったけど、ちょっと力技かなと思いました。
CIDR表記 → IPアドレス表記へ
まずはCIDR表記をIPアドレス表記へ変換する場合です。
ご存知の通り、実際に使用されるサブネットマスクの多くは「/8」「/16」「/24」のいずれかだったりします。
というわけで、この3つに関しては計算せずにif文で判定しています。
そして、計算で導出する場合は、以下の手順で計算します。
まず、「2^(CIDR表記のサブネットマスク)-1」によってマスク値の10進表記を求めます。
次にこれを2進表記に変換します。
するとCIDR表記の値の数だけ”1”が並んだものになります。
(例えば「/24」→111111111111111111111111)
そしてこの値が32ビットになるように末尾に"0"を足します。
(例えば「/24」→11111111111111111111111100000000)
あとは、8ビットづつ区切って10進数に変換してドット(.)で連結すれば完成です。
(11111111.11111111.11111111.00000000 → 255.255.255.0)
/** * サブネット(CIDER表記)をIPv4形式に変換する。 * * @param subnet * サブネット(CIDR表記) : 0〜32 * @return 結果文字列(IPv4形式のサブネットマスク) */ public static String convertSubnetCIDR2IPv4(final int subnet) { if (subnet < 0 || subnet > 32) { throw new IllegalArgumentException( "引数(subnet)は 0〜32 までの数値でなければなりません。(" + subnet + ")"); } // 典型的なサブネット形式は計算せずに導出 if (subnet % 8 == 0) { if (subnet == 8) { return "255.0.0.0"; } else if (subnet == 16) { return "255.255.0.0"; } else if (subnet == 24) { return "255.255.255.0"; } } // 汎用的なサブネット変換 final StringBuilder subnetBinary = new StringBuilder( Long.toBinaryString((long) (Math.pow(2, subnet) - 1))); for (int i = subnetBinary.length(); i <= 32; i++) { subnetBinary.append("0"); } final StringBuilder result = new StringBuilder(); for (int i = 8, length = subnetBinary.length(); i <= length; i += 8) { result.append(Integer.parseInt(subnetBinary.substring(i - 8, i), 2)) .append("."); } result.setLength(result.length() - 1); return result.toString(); }
IPアドレス表記 → CIDR表記
続いてIPアドレス表記をCIDR表記に変換する場合です。
計算方法は、CIDR表記→IPアドレス表記の逆ですね。
ただし、サブネットマスクの2進表記を求めた後は、先頭から"1"の数を数えることでCIDR表記の値を求めています。
また、ここを計算で導出する場合は、2進表記が32ビットになるように"0"を足して、
それを10進数(long)に変換し、+1します。
そして、そうやって求めた値の対数(低=2)をとると、計算によってCIDR表記の値を求めることができます。
低=2の対数を求める場合は「対数の変換公式」を用いましょう。
(忘れている方は思い出してね。難しくないので)
※ Javaで対数(低=10)を計算する場合は、java.lang.Math#log10(double) を使用します。
/** * サブネット(IPv4形式)をCIDR表記に変換する。 * * @param subnet * サブネット(IPv4形式) * @return 結果(CIDR表記の値) */ public static int convertSubnetIP2CIDRv4(final String subnet) { if (subnet == null || subnet.isEmpty()) { throw new IllegalArgumentException("引数(subnet)は nullまたは空文字です。"); } final String[] split = subnet.split("\\."); if (split.length != 4) { throw new IllegalArgumentException("引数(subnet)は IPv4形式ではありません。(" + subnet + ")"); } final StringBuilder sb = new StringBuilder(); for (String numStr : split) { final int num = Integer.parseInt(numStr); if (num < 0 || num > 255) { throw new IllegalArgumentException( "引数(subnet)は IPv4形式ではありません。(" + subnet + ")"); } sb.append(Integer.toBinaryString(num)); } final String subnetBinary = sb.toString(); int i = 0; while (subnetBinary.indexOf("1", i) != -1) { i++; } return i; }
補足
コード内のメソッド名の「To」を「2」と表記していますが、これは誤記ではありませんよ。
これは慣例のようなものですね。
私はあまり好きではないので、今回のように変換前の対象(CIDRやIP)が大文字の場合にしか使いませんけど。
私の好き嫌いはともかくとして、こういう命名の仕方もあることは、覚えておいてもいいのではないでしょうか。