ネットワークアドレスを求める!
たとえば、IPアドレス(IPv4)は32ビットで、32ビット内がネットワークアドレスとマシンアドレスになっています。
そして、IPアドレスをサブネットマスクするとネットワークアドレスが求まりますね。
つまり、任意のマシンが、どのネットワークアドレスに属するかは、IPアドレスをサブネットマスクで論理積すれば、分かるのです。
- '1'の文字は16進では『31』で2進数では『00110001』です。
- '0'の文字は16進では『30』で2進数では『00110000』です。
- 文字1『00110001』と文字0『00110000』の論理積は、文字0『00110000』になります。
- 文字1『00110001』と文字1『00110001』の論理積は、文字1『00110001』になります。
一方、Perlではビット演算子の論理積『&』がありますね。
1と0の文字の論理積の場合は、文字列でもビット演算子の論理積『&』が使えるのです!
たとえば、サブネットマスク255.255.255.0のIPアドレス172.11.22.33のネットワークアドレスは、172.11.22.0ですね。
これをPerlで計算すると以下の感じになります。
use strict; use warnings; # IPアドレスのドットアドレスをビット列の文字列へ変換 print dec2bin4IP('255.255.255.0'), "\n"; # IPアドレスのビット列の文字列をドットアドレスへ変換 print bin2dec4IP(dec2bin4IP('255.255.255.0')), "\n"; # マスクしてネットワークアドレスを求める print bin2dec4IP(dec2bin4IP('255.255.255.0') & dec2bin4IP('172.11.22.33')), "\n"; sub dec2bin4IP { my ($ip) = @_; my @aaa = split(/\./, $ip); my $bin = ''; for (my $i = 0; $i < 4; $i++) { $bin .= unpack("B8", pack("C", $aaa[$i])); } return $bin; } sub bin2dec4IP { my ($ip) = @_; $ip =~ /(........)(........)(........)(........)/; my $num1 = unpack("C", pack("B8",$1)); my $num2 = unpack("C", pack("B8",$2)); my $num3 = unpack("C", pack("B8",$3)); my $num4 = unpack("C", pack("B8",$4)); return "$num1.$num2.$num3.$num4"; }
上記は、サブネットマスクとIPアドレスをビット列の文字列へ変換して、その論理積をとって、ネットワークアドレスを取得し、ビット列の文字列からドットアドレスへ変換しています。
そして、その変換値、つまり、ネットワークアドレス 172.11.22.0 を表示しています。
尚、packは、引数をバイナリ値に変換し、unpackは、バイナリ値から元の値に変換します。
- pack("C", 文字列)は、文字列をバイナリ値に変換
- unpack("B8", バイナリ値)は、バイナリ値をビット8文字の文字列に変換
- pack("B8", ビット8文字の文字列)は、ビット8文字の文字列をバイナリ値に変換
- unpack("C", バイナリ値)は、バイナリ値を文字列に変換
追記
kitsさんから、もっとスマートな方法を教えて頂きました(ブックマークで…)。
my $ip = '255.255.255.0'; my $bin = sprintf "%08b"x4, split /\./, $ip; print $bin, "\n"; print join '.', map {oct "0b$_"} $bin=~/(\d{8})(\d{8})(\d{8})(\d{8})/;
『$bin=~/(\d{8})(\d{8})(\d{8})(\d{8})/』は、カッコを使った記憶がリストで返る(知らなかったです。得した気分です)。
それを"0b$_"で文字連結して、octで10進数にしてjoinする。