2017年07月26日

PHPでIPアドレス+ネットマスクに適合するかチェック

あるIPアドレスが、ネットワークアドレス/ネットマスク長にヒットするかどうかを検査したい。ソースアドレスでアクセス制限したいとかいう時によくあるアレである。
IPv4だけのときは、ip2long/long2ipでマスク長のビット演算をして比較すればよかったが、昨今IPv6も普通にあり得るのでそっちにも適用しなくちゃいけない。
IPv6は128ビットなので、longでは検査できないので、inet_ptonを使って検査するルーチンを書いてみた。

Github はこちら。
https://github.com/usoinfo/php_ipaddrmask_test

function ipaddr_mask($src, $mask)
{
$iarr = str_split($src);
$marr = str_split($mask);
$ret = false;
for($i=0;$i<count($iarr);$i++){
$ret .= $iarr[$i] & $marr[$i];
}
return $ret;
}

function ipaddr_compare($srcaddr, $netaddr, $netmask)
{
if( !$netmask && ($pos = strpos($netaddr,"/")) !== FALSE ){
$netmask = intval( substr($netaddr, $pos+1) );
$netaddr = substr($netaddr, 0, $pos);
}

$addr = inet_pton($srcaddr);
$comp = inet_pton($netaddr);

$len = strlen($addr)*8;
if($netmask > $len) $netmask = $len;

$mask = str_repeat('f', $netmask>>2);
switch($netmask & 3){
case 3: $mask .= 'e'; break;
case 2: $mask .= 'c'; break;
case 1: $mask .= '8'; break;
}
$mask = pack('H*', str_pad($mask, $len>>2, '0') );

return ipaddr_mask($addr, $mask) == ipaddr_mask($comp, $mask);
}

/*

使用法サンプル
$srcaddr = "192.168.1.12";
$netaddr = "192.168.1.0";
$netmask = 24;
$r = ipaddr_compare($srcaddr, $netaddr, $netmask);
echo $srcaddr." compare ".$netaddr."/".$netmask." => ".($r ? "match" : "unmatch")."\n";

$srcaddr = "25ef:91:c540:1:a00:27fa:fce1:28c";
$netaddr = "25ef:91:c540:1::0";
$netmask = 64;
$r = ipaddr_compare($srcaddr, $netaddr, $netmask);
echo $srcaddr." compare ".$netaddr."/".$netmask." => ".($r ? "match" : "unmatch")."\n";

実行結果
192.168.1.12 compare 192.168.1.0/24 => match
25ef:91:c540:1:a00:27fa:fce1:28c compare 25ef:91:c540:1::0/64 => match

*/

タグ:PHP
posted by usoinfo at 18:06 | Comment(0) | 開発 | このブログの読者になる | 更新情報をチェックする