Intereting Posts
Почему PHP strlen () возвращает отрицательную длину? Как я могу лучше понять уровень сервиса в symfony2 в php Реализация аутентификации Facebook Есть ли поддержка для Smarty в Aptana 3? Как наилучшим образом рассчитать расстояние между двумя городами (Канада)? PHP mkdir ($ recursive = true) пропускает последний каталог Учитываются ли комментарии при включении файлов в PHP? PHP: существует ли безопасный способ извлечения ($ _ POST) Добавление элемента в начало массива без изменения других ключей массива Код предназначен для загрузки файла excel (.xls) Как получить последнее значение автоматического прироста в качестве внешнего ключа для другой таблицы в php Лучший способ защитить от инъекций mysql и межсайтового скриптинга Как структурировать этот веб-проект Symfony? Когда нужно закрыть соединение с базой данных в PHP? Stripe – получить идентификатор клиента по электронной почте из формы

Соответствие адреса IPv6 для подсети CIDR

Есть ли хороший способ сопоставить адрес IPv6 с подсети IPv6 с использованием нотации CIDR? То, что я ищу, эквивалентно IPv6: сопоставление IP-маски с CIDR в PHP 5?

Приведенный выше пример не может быть использован, поскольку адрес IPv6 имеет длину 128 бит, что предотвращает правильную работу побитового сдвига слева. Можете ли вы подумать о другом?

EDIT: Добавлено мое собственное решение в список ответов.

Поскольку вы не можете конвертировать адреса IPv6 в integer, вы должны использовать биты, например:

$ip='21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'; $cidrnet='21DA:00D3:0000:2F3B::/64'; // converts inet_pton output to string with bits function inet_to_bits($inet) { $unpacked = unpack('A16', $inet); $unpacked = str_split($unpacked[1]); $binaryip = ''; foreach ($unpacked as $char) { $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); } return $binaryip; } $ip = inet_pton($ip); $binaryip=inet_to_bits($ip); list($net,$maskbits)=explode('/',$cidrnet); $net=inet_pton($net); $binarynet=inet_to_bits($net); $ip_net_bits=substr($binaryip,0,$maskbits); $net_bits =substr($binarynet,0,$maskbits); if($ip_net_bits!==$net_bits) echo 'Not in subnet'; else echo 'In subnet'; 

Кроме того, если вы используете некоторую базу данных для хранения IP-адресов, возможно, у нее уже есть все функции для их сравнения. Например, Postgres имеет тип inet и может определить, содержится ли IP в подсети следующим образом:

 SELECT '21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'::inet << '21DA:00D3:0000:2F3B::/64'::inet; 

9,11. Функции и операторы сетевых адресов в PostgreSQL

Если ваши маски всегда делятся на четыре (что довольно часто встречается в ipv6). Вы можете использовать:

 function checkIPv6WithinRange($ipv6, $range) { list ($net, $mask) = preg_split("/\//", $range); if ($mask % 4) throw new NotImplementedException("Only masks divisible by 4 are supported"); $stripChars = (128-$mask)/4; $hexNet = bin2hex(inet_pton($net)); $reducedNet = substr($hexNet, 0, 0 - $stripChars); $hexIp = bin2hex(inet_pton($ipv6)); $reducedIp = substr($hexIp, 0, 0 - $stripChars); return $reducedIp === $reducedNet; } 

Вы также можете использовать класс IpUtils из пакета symfony / http-foundation :

 IpUtils::checkIp6('2a01:8760:2:3001::1', '2a01:8760:2:3001::1/64') 

Это проверит соответствие действительности IPv6 и соответствие диапазона. Вернет false если это не так.

Я создал свое собственное решение, используя следующий код:

 function iPv6MaskToByteArray($subnetMask) { $addr = str_repeat("f", $subnetMask / 4); switch ($subnetMask % 4) { case 0: break; case 1: $addr .= "8"; break; case 2: $addr .= "c"; break; case 3: $addr .= "e"; break; } $addr = str_pad($addr, 32, '0'); $addr = pack("H*" , $addr); return $addr; } function iPv6CidrMatch($address, $subnetAddress, $subnetMask) { $binMask = iPv6MaskToByteArray($subnetMask); return ($address & $binMask) == $subnetAddress; } 

Обратите внимание, что $ address и $ subnetAddress были получены путем запуска строкового адреса через inet_pton. Вызвать функцию следующим образом:

 $subnet = inet_pton("2001:06b8::"); $mask = 32; $addr = inet_pton("2001:06b8:0000:0000:0000:0000:1428:07ab"); $match = iPv6CidrMatch($addr, $subnet, $mask); // TRUE 

В основном http://www.phpclasses.org/browse/file/70429.html

Чтобы использовать его, просто позвоните

 $cidr = new CIDR(); $cidr->match($ipv6, $ipv6_in_cidr); 

Результат «Хорошо».

Вот пример, который работает, проверяя IP-адрес на список отдельных IP-адресов или CIDR, как IPv4, так и IPv6:

https://gist.github.com/lyquix-owner/2620da22d927c99d57555530aab3279b

 <?php // IP to check $ip_check = $_SERVER['REMOTE_ADDR']; // Array of allowed IPs and subnets, both IPv4 and IPv6 $ips_allowed = array( '192.30.252.0/22' '2620:112:3000::/44' '192.168.16.104' ); // Flag for IP match allowed list $ip_match = false; foreach($ips_allowed as $ip_allow) { // If IP has / means CIDR notation if(strpos($ip_allow, '/') === false) { // Check Single IP if(inet_pton($ip_check) == inet_pton($ip_allow)) { $allow = true; break; } } else { // Check IP range list($subnet, $bits) = explode('/', $ip_allow); // Convert subnet to binary string of $bits length $subnet = unpack('H*', inet_pton($subnet)); // Subnet in Hex foreach($subnet as $i => $h) $subnet[$i] = base_convert($h, 16, 2); // Array of Binary $subnet = substr(implode('', $subnet), 0, $bits); // Subnet in Binary, only network bits // Convert remote IP to binary string of $bits length $ip = unpack('H*', inet_pton($ip_check)); // IP in Hex foreach($ip as $i => $h) $ip[$i] = base_convert($h, 16, 2); // Array of Binary $ip = substr(implode('', $ip), 0, $bits); // IP in Binary, only network bits // Check network bits match if($subnet == $ip) { $allow = true; break; } } } if(!$allow) { die('IP not allowed'); }