ip intervalle -> séquences sous-réseaux

Petit nouveau ! | 4 Messages

03 août 2006, 13:13

Bonjour,

Après avoir cherché sur les php FAQ, dans les "user contributions" liées à la ip2long, ainsi que sur http://svn.netfilter.org/cgi-bin/viewcv ... s/iprange/ pour une solution similaire, voilà mon problème : je dispose d'un intervalle compact d'adresses IP : 10.0.0.1-10.0.0.30 ; comment puis-je le transformé dans la séquence [10.0.0.1/28,10.0.0.16/29,10.0.0.24/30,10.0.0.28/31,10.0.0.32/32],
car
10.0.0.1/28 = 10.0.0.1-10.0.0.15
10.0.0.16/29 = 10.0.0.16-10.0.0.23
10.0.0.24/30 = 10.0.0.24-10.0.0.27
10.0.0.28/31 = 10.0.0.28-10.0.0.29
10.0.0.30/32 = 10.0.0.30

J'aurais besoin d'un algo pour la transformation antérieure ; est-ce qu'il y a qch disponible, quelque part , une petite idée ? j'y ai pensé mais ça donne une complexité exorbitante...

merci

ViPHP
ViPHP | 1380 Messages

03 août 2006, 15:25

Il me me semble que ta conversion CIDR -> IP range n'est pas correcte. Voici ce que j'obtiens avec un bout de code trouvé dans la doc ainsi que sur ce site:
http://jodies.de/ipcalc

Code : Tout sélectionner

10.0.0.1/28 = Range: 10.0.0.1 -> 10.0.0.14 (14 hosts) 10.0.0.16/29 = Range: 10.0.0.17 -> 10.0.0.22 (6 hosts) 10.0.0.24/30 = Range: 10.0.0.25 -> 10.0.0.26 (2 hosts) 10.0.0.28/31 = Range: 10.0.0.29 -> 10.0.0.28 (0 hosts) 10.0.0.30/32 = Range: 10.0.0.31 -> 10.0.0.29 (-1 hosts)
Le code:
function cidr2ipRange ($ip_addr_cidr) {
  $ip_arr = explode('/', $ip_addr_cidr);
  $bin = '';
  for($i=1;$i<=32;$i++) {
     $bin .= $ip_arr[1] >= $i ? '1' : '0';
  }
  $ip_arr[1] = bindec($bin);
  $ip = ip2long($ip_arr[0]);
  $nm = ip2long($ip_arr[1]);
  $nw = ($ip & $nm);
  $bc = $nw | (~$nm);
  $ret['nbHost'] = ($bc - $nw - 1);
  $ret['range']  = long2ip($nw + 1) . " -> " . long2ip($bc - 1);
  return $ret;
}  

$cidr = array(
'10.0.0.1/28',
'10.0.0.16/29',
'10.0.0.24/30',
'10.0.0.28/31',
'10.0.0.30/32');

echo '<pre>';
foreach ($cidr as $v) {
  $ret = cidr2ipRange($v);
  echo $v . " = Range: " . $ret['range']. ' ('. $ret['nbHost'] .' hosts)' . "\n";
}
echo '</pre>';
Si j'ai bien compris, c'est dans l'autre sens que tu veux faire la conversion?
ripat

Petit nouveau ! | 4 Messages

03 août 2006, 15:48

Merci, mais :

En fait, je n'ai pas bien marqué : 10.0.0.1/28 ne peut pas exister, car une adresse réseau doit se terminer avec des "zeros" ; donc, il fallait que je mette 10.0.0.0/28 ; cela donne les derniers 4 bits exploitables pour construir les adresses des machines du réseau 10.0.0.0/28 ; 4bits=>16 combinaison; en éliminant une, 10.0.0.0 (non -attribuable à une machine) => 10.0.0.0/28 = l'intervalle [10.0.0.1 ... 10.0.0.15 ] ; donc, ma faute d'avoir mis 10.0.0.1/28, mais cela à cause du 10.0.0.0-non attribuable. Par contre 10.0.0.30/32=10.0.0.32 (mask 32=>host, pas d'intervalle).

Enfin, la conversion {adr_sousRéseau/mask --> intervalle d'adresses IP} est banale, donc ce qui m'interesse est de passer comme input X=[minIP ; maxIP] et d'obtenir comme output la séquence Y = [adr1/mask1, adr2/mask2, adr3/mask3,...] avec [b]X=Y[/b] .

Encore une fois, merci.

ViPHP
ViPHP | 2144 Messages

03 août 2006, 15:51

En règle général, tu ne peux pas attribuer l'adresse la plus haute du range (celle dont tous les bits sont à 1) car c'est l'adresse broadcast: envoit à tous les ordinateurs du réseau.

Petit nouveau ! | 4 Messages

03 août 2006, 16:09

oui, tout à fait. Mais je peut prendre en compte les adresse de diffusion, lors de la configuration d'un IDS qui alerte sur le messages ayant l'adresse dont vous parlez ; d'où l'interes de l'avoir.
Enfin, ce n'est pas là la difficulté, c'est l'algo que je viens de décrire.
merci

ViPHP
ViPHP | 1380 Messages

03 août 2006, 16:13

Trouvé ceci dans le cache de Google:
function imask($this){
  return base_convert((pow(2,32) - pow(2, (32-$this))), 10, 16);
}

function imaxblock($ibase, $tbit){
  while ($tbit > 0){
    $im = hexdec(imask($tbit-1));
    $imand = $ibase & $im;
    if ($imand != $ibase) {
      break;
    }
    $tbit--;
  }
  return $tbit;
}

function ip2cidr ($istart, $iend) {
  $s = explode(".", $istart);
  $start = "";
  $dot = "";
  while (list($key, $val) = each($s)) {
    $start = sprintf("%s%s%d", $start, $dot, $val);
    $dot = ".";
  }
  $end = "";
  $dot = "";
  $e = explode(".", $iend);
  while (list($key,$val) = each($e)) {
    $end = sprintf("%s%s%d", $end, $dot, $val);
    $dot = ".";
  }
  $start  = ip2long($start);
  $end    = ip2long($end);
  $result = array();
  while ($end > $start) {
    $maxsize = imaxblock($start,32);
    $x = log($end - $start + 1)/log(2);
    $maxdiff = floor(32 - floor($x));
    $ip = long2ip($start);
    if ($maxsize < $maxdiff){
      $maxsize = $maxdiff;
    }
    array_push($result, "$ip/$maxsize");
    $start += pow(2, (32-$maxsize));
  }
  return $result;
}

$a = ip2cidr('192.168.0.158' , '192.168.0.255');
echo '<pre>'; print_r ($a); echo '</pre>';
Je n'en suis pas l'auteur, donc ne me pose pas trop de questions! :wink:

http://66.249.93.104/search?q=cache:yDH ... =clnk&cd=1
ripat

Administrateur PHPfrance
Administrateur PHPfrance | 3131 Messages

03 août 2006, 16:17

Voici une façon de procéder, d'abord écrire en binaire :

Code : Tout sélectionner

00001010 00000000 00000000 00000001 00001010 00000000 00000000 00011110
ça va donc se jouer sur un masque de 5 bits, donc /27 maximum.
En testant /27 on se rend compte que c'est la solution, puisque tu n'as pas pensé qu'il fallait exclure le network (00000) et le broadcast (11111).

Pour ton problème ta résolution est déjà erronée puisqu'il s'agit de 10.0.0.0/27.

Pour ton problème général, je ne suis pas sûr que ce soit possible. Je pense qu'il y a des découpages ou tu vas te retrouver avec une série complète de "single-host" (/32) et donc pas du tout intéressant. Mais ça mérite de se pencher dessus.

Edit : après recherche, il y a une méthode pour les plages réseaux quelconques (mais des vraies plages réseau, 0 à 255), cela n'implique pas qu'on puisse l'appliquer à toute séquence d'adresses IP :
6.5 Comment découper une plage réseau quelconque comme somme
de plusieurs plages ?

Nous avons vu qu'une plage réseau ne pouvait pas être
choisie n'importe comment.
Etant donné que les masques et les adresses IP se basent
sur un codage binaire, les chiffres utilisés dans les
adresses résultantes ne pourront être que des multiples de
puissances de 2 en accord avec le masque.
Ainsi, une plage 70.0.0.0 ne pourra pas avoir un masque qui
définisse plus de deux chiffres sur le premier octet, car
70 est un multiple de 2, mais pas de 4.

Ce n'est pas clair ? un exemple devrait vous aider à mieux
comprendre.

Disons que l'on veut décrire la plage d'adresses allant de
69.0.0.0 à 79.255.255.255.

La question est de savoir quel masque associé à quelle
adresse de réseau nous permettra de définir cette plage.

Le premier octet varie de 69 à 79. Il prend donc 11
valeurs.
11 n'étant pas une puissance de 2, on sait d'ores et déjà
que l'on ne pourra pas définir cette plage avec un seul
réseau, mais qu'il va falloir la découper en une somme de
plusieurs réseaux.
Le but est cependant d'optimiser cette somme de réseaux
pour en avoir le moins possible. On pourrait simplement
utiliser 11 réseaux avec des masques 255.0.0.0, mais on
doit surement pouvoir faire plus propre et regrouper
plusieurs de ces réseaux en un seul.

La première puissance de 2 inférieure à 11 est 8. Il faut
maintenant savoir si l'on peut placer un réseau, dont le
premier octet décrira 8 valeurs, dans cette plage. Le seul
multiple de 8 de cette plage est 72. On décrirait alors un
réseau dont le premier octet varierait de 72 à 79, ce qui
est bien compris dans notre plage d'origine.
Le réseau 72.0.0.0/248.0.0.0 est donc bien adapté pour
décrire notre plage, mais il reste encore à décrire les
adresses de 69.0.0.0 à 71.255.255.255.

On effectue le même raisonnement.
(Ici le premier octet prend 3 valeurs, la puissance de 2
inférieure à 3 est 2, et le multiple de 2 de cette plage
est 70)
On trouve donc le réseau 70.0.0.0/254.0.0.0

Il ne nous reste plus qu'à décrire la plage 69.0.0.0 à
69.255.255.255 qui peut être définie par
69.0.0.0/255.0.0.0.

Et voilà !!
Nous avons découpé notre plage d'origine qui allait de
69.0.0.0 à 79.255.255.255 en trois sous réseaux:
69.0.0.0/255.0.0.0
70.0.0.0/254.0.0.0
et 72.0.0.0/248.0.0.0
Source: google "décomposer plage ip sous réseau"

Petit nouveau ! | 4 Messages

03 août 2006, 17:40

[quote="Ripat"]Trouvé ceci dans le cache de Google:

[php]function imask($this){
return base_convert((pow(2,32) - pow(2, (32-$this))), 10, 16);
}

function imaxblock($ibase, $tbit){
while ($tbit > 0){
$im = hexdec(imask($tbit-1));
$imand = $ibase & $im;
if ($imand != $ibase) {
break;
}
$tbit--;
}
return $tbit;
}

function ip2cidr ($istart, $iend) {
$s = explode(".", $istart);
$start = "";
$dot = "";
while (list($key, $val) = each($s)) {
$start = sprintf("%s%s%d", $start, $dot, $val);
$dot = ".";
}
$end = "";
$dot = "";
$e = explode(".", $iend);
while (list($key,$val) = each($e)) {
$end = sprintf("%s%s%d", $end, $dot, $val);
$dot = ".";
}
$start = ip2long($start);
$end = ip2long($end);
$result = array();
while ($end > $start) {
$maxsize = imaxblock($start,32);
$x = log($end - $start + 1)/log(2);
$maxdiff = floor(32 - floor($x));
$ip = long2ip($start);
if ($maxsize < $maxdiff){
$maxsize = $maxdiff;
}
array_push($result, "$ip/$maxsize");
$start += pow(2, (32-$maxsize));
}
return $result;
}

$a = ip2cidr('192.168.0.158' , '192.168.0.255');
echo '<pre>'; print_r ($a); echo '</pre>';
[/php]

Je n'en suis pas l'auteur, donc ne me pose pas trop de questions! :wink:

http://66.249.93.104/search?q=cache:yDH ... =clnk&cd=1[/quote]


Hmmm, je l'ai essayé pour qq intervalles et je pense qu'il fait ce dont j'ai besoin. Merci bien