Lo primero, dar las gracias a Paul Gregg y a su entrada entrada PHP algorithms: Determining if an IP is within a specific range que si no tenéis problemas con el inglés, os recomiendo encarecidamente que la leáis porque todo lo que os voy a contar viene muchísimo mejor explicado y documentado.
Hasta ahora no me he encontrado con la necesidad de filtrar una IP entre un listado de rangos muy amplios y cada uno especificado de formas distintas. Estuve volviéndome loco buscando por varios foros en los que partían una IP en trozos luego la rellenaban con ceros y luego la intentaban transformar… vamos una chapuza.
La solución me la dio Paul, una IP se puede pasar de decimal a binario 32bit (impresionante una sola línea de código substituye a varias decenas):
1 2 3 4 5 6 |
/** * Convertir una ip de decimal a binario */ function decbin32($dec) { return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT); } |
Y ahora el siguiente problema, comparar nuestra IP con un array de rangos representados de distinta forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
/** * @param: string $ip * @param: string $range * * Devuelve si una IP está dentro de un rango especificado como: * 1. Comodines: 1.2.3.* * 2. CIDR: 1.2.3/24 ó 1.2.3.4/255.255.255.0 * 3. Ip de inicio y de fin: 1.2.3.0-1.2.3.255 * @return: BOOLEAN */ function ip_in_range($ip, $range) { if (strpos($range, '/') !== false) { // $range is in IP/NETMASK format list($range, $netmask) = explode('/', $range, 2); if (strpos($netmask, '.') !== false) { // $netmask is a 255.255.0.0 format $netmask = str_replace('*', '0', $netmask); $netmask_dec = ip2long($netmask); return ((ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec)); } else { // $netmask is a CIDR size block // fix the range argument $x = explode('.', $range); while (count($x) < 4) $x[] = '0'; list($a, $b, $c, $d) = $x; $range = sprintf("%u.%u.%u.%u", empty($a) ? '0' : $a, empty($b) ? '0' : $b, empty($c) ? '0' : $c, empty($d) ? '0' : $d); $range_dec = ip2long($range); $ip_dec = ip2long($ip); # Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s #$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0')); # Strategy 2 - Use math to create it $wildcard_dec = pow(2, (32 - $netmask)) - 1; $netmask_dec = ~$wildcard_dec; return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec)); } } else { // range might be 255.255.*.* or 1.2.3.0-1.2.3.255 if (strpos($range, '*') !== false) {// a.b.*.* format // Just convert to A-B format by setting * to 0 for A and 255 for B $lower = str_replace('*', '0', $range); $upper = str_replace('*', '255', $range); $range = "$lower-$upper"; } if (strpos($range, '-') !== false) {// A-B format list($lower, $upper) = explode('-', $range, 2); $lower_dec = (float)sprintf("%u", ip2long($lower)); $upper_dec = (float)sprintf("%u", ip2long($upper)); $ip_dec = (float)sprintf("%u", ip2long($ip)); return (($ip_dec >= $lower_dec) && ($ip_dec <= $upper_dec)); } // echo 'Range argument is not in 1.2.3.4/24 or 1.2.3.4/255.255.255.0 format'; return false; } } |
Comments are closed.