IP range to CIDR (php function script)

Ich habe schon eine Weile danach gesucht, eine PHP-Funktion, die eine IP Range in eine CIDR (Classless Inter-Domain Routing) Notation umwandelt. Wer über dieses Verfahren mehr wissen will, es gibt genügend Info dazu im Internet.

Der Vorteil in der Kurzschreibweise sind hauptsächlich deutlich kürzere IP-Listen – und somit interessant für Webmaster, wenn sie denn mal bestimmte IP Adressen oder gar Ranges vom Zugriff sperren wollen. Das kann wichtig sein für Apache Webserver Admins, die eine .htaccess pflegen! Mehr über eine .htaccess hier.

Ein Beispiel soll das zeigen: Zunächst der Link.
Gehen wir mal von folgendem aus, ich möchte eine IP Range sperren und in die .htaccess eintragen:
85.114.128.0 – 85.114.159.255 (diese Range ist Deutschland zugeordnet)
CIDR = 85.114.128.0/19

Statt einer Vielzahl von IP Adressen genügt der Eintrag „deny from 85.114.128.0/19“ in der .htaccess. Das ist alles, ab sofort wird der gesamten IP-Range der Zugang auf die Webseite verwehrt! Auf diese Weise lassen sich ganze Listen erstellen, um regelrecht sogar Länder auszusperren!

Die Funktion sieht so aus:

function range2cidr($ip_start, $ip_end) {

    static $cidr2hosts;

    if(empty($cidr2hosts)){

        $cidr2hosts = array(
            32 => 1,
            31 => 2,
            30 => 4,
            29 => 8,
            28 => 16,
            27 => 32,
            26 => 64,
            25 => 128,
            24 => 256,
            23 => 512,
            22 => 1024,
            21 => 2048,
            20 => 4096,
            19 => 8192,
            18 => 16384,
            17 => 32768,
            16 => 65536,
            15 => 131072,
            14 => 262144,
            13 => 524288,
            12 => 1048576,
            11 => 2097152,
            10 => 4194304,
            9  => 8388608,
            8  => 16777216,
            7  => 33554432,
            6  => 67108864,
            5  => 134217728,
            4  => 268435456,
            3  => 536870912,
            2  => 1073741824,
            1  => 2147483648,
            0  => 4294967296
        );
    }

    $arr_start = explode(".", $ip_start);
    $arr_end = explode(".", $ip_end);

    $bin_start = "";
    $bin_end = "";

    foreach($arr_start as $a){

        $bin_start.= str_pad(decbin($a), 8, "0", STR_PAD_LEFT);
    }

    foreach($arr_end as $a){
        $bin_end.= str_pad(decbin($a), 8, "0", STR_PAD_LEFT);
    }

    $cidr = 0;
    for($x=0;$x<strlen($bin_start);$x++){
        if($bin_start[$x] == $bin_end[$x]){
            $cidr++;
        } else {
            break;
        }
    }

    $host_count = ~(ip2long($ip_start) ^ ip2long($ip_end)) * -1;

    $cidrs = array();

    if($cidr2hosts[$cidr] != $host_count){

        // we have a non-cidr range, break it up and find them all

        $ranges = array();

        $new_end = long2ip(ip2long($ip_end) - ($cidr2hosts[$cidr] - $host_count));

        $ranges[] = array($ip_start, $new_end);

        $new_start = long2ip(ip2long($new_end) + 1);
        $ranges[] = array($new_start, $ip_end);

        foreach($ranges as $range){
            $cidrs = array_merge($cidrs, call_user_func_array("range2cidr", $range));
        }

    } else {

        $cidrs = array("$ip_start/$cidr");

    }

    return $cidrs;
}

Ich gebe zu, so ganz habe ich das Verfahren nicht verstanden aber es funktioniert bestens mit IP4 soweit ich das mit Online IP2CIDR Seiten vergleichen konnte! Als kleines Hauptprogamm würde ich z.B. folgendes tun:

<?php
//  ---------------------------------
	include 'iprange2cidr.php'; 
//	Werte übergeben:
	$min = $_POST["fip"];
	$max = $_POST["lip"];
//  ---------------------------------
	$resultarray = range2cidr($min, $max);			// result array übergeben
	$result = count(range2cidr($min, $max));		// array indizes errechnen

	echo "IP RANGE:<br>$min - $max<br><br>CIDR: <br>";

	for($i=0; $i < $result; $i++)
   	{
   	echo "$resultarray[$i]<br>";
   	}
?> 

Die PHP-Funktion speichert man z.B. in eine Datei ab mit dem Namen iprange2cidr.php was den Vorteil hat, ich muss den ganzen Code nicht im Hauptprogramm mitschleifen, das Einbinden macht das „include“ zu Beginn.
Man übergibt von einer Webseite aus die Start und Ende-IP Adresse, die Funktion errechnet einen neuen $resultarray, der aus mehreren Ergebnissen bestehen kann! Mit $result ermittle ich die Größe des Arrays und gebe das über eine for-Schleife aus. Das war es dann schon.

Die Range 173.242.117.84 – 173.242.207.255 ergibt folgendes Ergebnis:

IP RANGE:
173.242.117.84 – 173.242.207.255

CIDR:
173.242.117.84/16
173.242.138.172/19
173.242.149.84/18
173.242.170.172/19
173.242.181.84/17
173.242.202.172/21
173.242.205.84/22
173.242.206.172/23
173.242.207.84/24
173.242.207.172/25
173.242.207.212/26
173.242.207.236/27
173.242.207.244/28
173.242.207.252/30

GeoLite bietet z.B. eine nahezu komplette IP2Country Datei an (im csv Fomat), die man in eine SQL Datenbank importieren kann. Anhand einer IP Adresse ermittelt man dann die Range. Werde das an anderer Stelle noch erklären!


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*