Arbeiten Sie mit IPv6 in PHP

Wir haben kürzlich den LIR-Status und / 29 IPv6-Block erhalten. Und dann mussten die festgelegten Subnetze nachverfolgt werden. Und da unsere Abrechnung in PHP geschrieben war, musste ich mich ein wenig von dem Problem inspirieren lassen und feststellen, dass diese Sprache im Hinblick auf die Arbeit mit IPv6 nicht die freundlichste ist. Under the cut - unsere Lösung für die Probleme bei der Arbeit mit Adressen und Bereichen. Möglicherweise nicht das eleganteste, aber es führt die Aufgaben durch.



Ein bisschen Theorie


Haftungsausschluss. Wenn Sie wissen, was IPv6 ist und womit es gegessen wird, kann dieser Teil für Sie langweilig sein. Es kann nicht sein.

Personen, die die IPv6-Annotation zum ersten Mal sehen, werden möglicherweise entmutigt. Nach dem eleganten 64.233.177.101 stoßen wir plötzlich auf 2607: f8b0: 4002: c08 :: 8b und können verwirrt werden. Sowohl das als auch eine andere - nur für Menschen lesbare Darstellung von 32 bzw. 128 Bits. Jedes IP-Paket enthält einen Header mit einer streng standardisierten Zuweisung jedes Bits. Ohne noch tiefer in die Struktur der Header einzusteigen, müssen wir eines herausholen: Für Operationen mit IP-Adressen und -Bereichen ist es im Allgemeinen bequem, binäre Mathematik und bitweise Operationen zu verwenden. Es ist auch am bequemsten, sie in der Datenbank als BINARY (4) für IPv4 und BINARY (16) für IPv6 zu speichern.

Ein weiterer wichtiger Aspekt, der angesprochen werden sollte, sind Netzwerkmasken und die CIDR-Notation. CIDR ist eine Abkürzung für Classless Inter-Domain Routing. Dieses Konzept ersetzte die erste Klasse bei der Bestimmung, welcher Teil der IP-Adresse das Netzwerkpräfix und welcher Teil die Netzwerkschnittstellenadresse innerhalb dieses Netzwerks ist. In der Praxis werden die ersten n Bits, die dem Präfix entsprechen, auf 1 gesetzt, der Rest auf 0.

In lesbarer Form ist dies als ip.add.re.ss. / cidr geschrieben . Zum Beispiel bedeutet 64.233.177.0/24 , dass sich die ersten 24 Bits auf das Präfix beziehen. Die letzten 8 Bits, die letzte Nummer in einem für Menschen lesbaren Eintrag, beziehen sich auf die Adresse innerhalb des Subnetzes. Noch ein paar Übungen. 64.233.177.101/32 und 2607: f8b0: 4002: c08 :: 8b / 128 - eine bestimmte Adresse. 2607: f8b0: 4002: c08 :: / 64 - die ersten 64 Bits (die ersten 4 Gruppen) - das Präfix, die restlichen 64 Bits - der lokale Teil. Übrigens, wenn jemand durch das "::" im Eintrag in Verlegenheit gebracht wird, ersetzt ein Doppelpunkt eine beliebige Anzahl von Abschnitten, die 0 enthalten. Sie kann in der Annotation nur einmal vorkommen. Mit anderen Worten, 2607: f8b0: 4002: c08 :: 8b = 2607: f8b0: 4002: c08: 0: 0: 0: 8b .

Was müssen wir daraus lernen? Erstens können die erste und die letzte Subnetzadresse unter Verwendung von binärem UND und ODER erhalten werden, wobei die Maske in binärer Form bekannt ist. Zweitens kann das nächste Teilnetz der Größe (d. H. Mit CIDR) n berechnet werden, indem 1 zur n- ten Position in binärer Darstellung addiert wird. Mit binärer Sicht meine ich das Ergebnis der Verwendung der Funktionen pack () und inet_pton () und die weitere Verwendung von bitweisen Operatoren durch binär - eine Darstellung im binären System, die beispielsweise mit base_convert () erhalten werden kann .

Historischer Hintergrund
Der klassenlosen Trennung der Adressierung ging die klassenlose voraus. In jenen fernen Jahren hatte niemand damit gerechnet, dass es so viele Subnetze geben würde, sie waren rechts und links in großen Blöcken verteilt: Klasse A - die ersten 8 Bits (d. H. Die erste Zahl) wurden mit dem führenden Bit 0 vorangestellt; Klasse B - die ersten 16 (die ersten beiden Zahlen), die führenden Bits von 10; Klasse C - die ersten 24 Bits, die führenden Bits von 110. Diese führenden Bits legen die Bereiche fest, in denen die Adresse einer Klasse ausgegeben wurde: 0.0.0.0 - 127.255.255.255 für Klasse A, 128.0.0.0 - 191.255.255.255 - Klasse B, 192.0 .0.0 - 223.255.255.255 - Klasse C. Als sich das Internet rund um den Planeten ausbreitete, stellten die Regulierungsbehörden fest, dass sie es versäumt hatten, und entwickelten in den frühen 90er Jahren ein klassenloses Konzept, das es ihnen ermöglichte, sich nicht an die führenden Bits zu binden. Ein bisschen mehr Details findet man zum Beispiel im Großen und Allwissenden .


Lass uns weiter üben


In der Praxis setzen wir die drei wahrscheinlichsten Aufgaben um, wie es mir schien:

  1. Abrufen der ersten und letzten Adresse des Bereichs;
  2. Erhalten des nächsten Bereichs einer gegebenen Größe (CIDR);
  3. Überprüfen Sie, ob die Adresse zu einem Bereich gehört.

Die Implementierung erfolgt für IPv6, die Logik kann jedoch bei Bedarf einfach angepasst werden. Ich habe einige Ideen von hier , aber ein wenig anders umgesetzt. Auch in den Beispielen wird nicht auf Eingabefehler geprüft. Also lass uns gehen.

Wie bereits erwähnt, können die erste und die letzte Adresse eines Bereichs mithilfe von bitweisen Operationen ermittelt werden, wobei der Bereichsanfang und die binäre Subnetzmaske bekannt sind. Dementsprechend müssen wir zuerst CIDR in eine binäre Maske umwandeln. Sammeln Sie dazu die hexadezimale Darstellung und packen Sie sie in eine Binärdatei.

function cidrToMask ($cidr) { $mask = str_repeat('f', ceil($cidr / 4)); $mask .= dechex(4 * ($cidr % 4)); $mask = str_pad($mask, 32, '0'); return pack('H*', $mask); } 

Call Pack ('H *', $ mask) packt die hexadezimale Darstellung auf dieselbe Weise wie inet_pton () . Der einzige Unterschied besteht darin, dass beim Aufruf von pack () alle 0 vorhanden sein müssen und der Eintrag im Gegensatz zum lesbaren Eintrag keinen Doppelpunkt enthalten darf.

Der nächste Schritt besteht darin, den Anfang und das Ende des Bereichs zu berechnen. Und hier gibt es Nuancen. Bitweise Operationen sind durch die Prozessorkapazität begrenzt. Dementsprechend können auf meinem 32-Bit-CubieTruck, den ich manchmal für Testverwöhnungen verwende, nicht alle 128 Bit der Adresse in einem Vorgang verarbeitet werden. Nichts hindert uns jedoch daran, es in 32-Bit-Gruppen aufzuteilen (nur für den Fall, wer weiß, auf welchen Prozessoren wir ausgeführt werden).

 function getRangeBoundary ($ip, $cidr, $which, $ipIsBin = false, $returnBin = false) { $mask = cidrToMask($cidr); if (!$ipIsBin) { $ip = inet_pton($ip); } $ipParts = str_split($ip, 4); $maskParts = str_split($mask, 4); $rangeParts = []; for ($i = 0; $i < count($ipParts); $i++) { if ($which == 'start') { /*  &       . */ $rangeParts[$i] = $ipParts[$i] & $maskParts[$i]; } else { /*  |    (~)           1. */ $rangeParts[$i] = $ipParts[$i] | ~$maskParts[$i]; } } $rangeBoundary = implode($rangeParts); if ($returnBin) { return $rangeBoundary; } else { return inet_ntop($rangeBoundary); } } 

Für die zukünftige Verwendung bieten wir die Möglichkeit, IP zu übertragen und das Ergebnis sowohl in binärer als auch in lesbarer Form zu erhalten. Der $ which- Parameter hier legt fest, ob wir den Anfang oder das Ende des Bereichs abrufen möchten (die Werte sind 'start' bzw. 'end' ).

Die nächste Aufgabe (neben der für unser Unternehmen praktischsten) ist die Berechnung des nächsten Bereichs. Für diese Aufgabe fiel mir nichts Besseres ein, als wie man die Adresse in einen Binärstring zerlegt und an der gewünschten Position 1 addiert und dann alles zurückklappt. Um zu verhindern, dass Artefakte irgendwo auftauchen, habe ich mich entschlossen, die Adresse während der Zerlegung und Assemblierung nach Bytes zu teilen.

 function getNextBlock ($ipStart, $cidr, $ipIsBin = false, $returnBin = false) { if (!$ipIsBin) { $ipStart = inet_pton($ipStart); } $ipParts = str_split($ipStart, 1); $ipBin = ''; foreach ($ipParts as $ipPart) { $ipBin .= str_pad(base_convert(unpack('H*', $ipPart)[1], 16, 2), 8, '0', STR_PAD_LEFT); } /*  1       "" :) */ $i = $cidr - 1; while ($i >= 0) { if ($ipBin[$i] == '0') { $ipBin[$i] = '1'; break; } else { $ipBin[$i] = '0'; } $i--; } $ipBinParts = str_split($ipBin, 8); foreach ($ipBinParts as $key => $ipBinPart) { $ipParts[$key] = pack('H*', str_pad(base_convert($ipBinPart, 2, 16), 2, '0', STR_PAD_LEFT)); } $nextIp = implode($ipParts); if ($returnBin) { return $nextIp; } else { return inet_ntop($nextIp); } } 

Die Ausgabe ist das Präfix des nächsten in $ cidr angegebenen Größenbereichs . Mit dieser Funktion vergeben wir Adressblöcke an unsere Kunden.

Überprüfen Sie abschließend, ob die Adresse zum Bereich gehört. Zum Beispiel haben wir einen / 48-Block für die Verteilung von / 64-Blöcken an Kunden zugewiesen, und wir müssen sicherstellen, dass wir während des Termins nicht über den zugewiesenen Block hinausgehen (in der Praxis wird dies bald geschehen, aber es besteht immer noch eine Chance). Hier ist alles einfach. Wir erhalten den Anfang und das Ende des Bereichs in binärer Form und prüfen, ob die Adresse innerhalb liegt.

 function ipInRange ($ip, $rangeStart, $cidr) { $start = getRangeBoundary($rangeStart, $cidr, 'start',false, true); $end = getRangeBoundary($rangeStart, $cidr, 'end',false, true); $ipBin = inet_pton($ip); return ($ipBin >= $start && $ipBin <= $end); } 

Hoffe es war hilfreich. Welche anderen Adressierungsfunktionen könnten für Sie hilfreich sein? Ergänzungen, Kommentare und Codeüberprüfungen sind in den Kommentaren ausdrücklich erwünscht.

Wenn Sie bereits Kunde bei uns sind oder nur daran denken, einer zu werden, empfehlen wir Ihnen anlässlich der Veröffentlichung dieses Artikels, block / 64 kostenlos für alle vps-Dienste oder einen dedizierten Server im Equinix Tier IV-Rechenzentrum in den Niederlanden zu erhalten, indem Sie auf Anfrage an die Vertriebsabteilung einen Link zu senden dieser Artikel im Ticket. Das Angebot gilt bis März 2020.

Ein bisschen Werbung :)


Vielen Dank für Ihren Aufenthalt bei uns. Mögen Sie unsere Artikel? Möchten Sie weitere interessante Materialien sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder Ihren Freunden Cloud-basiertes VPS für Entwickler ab 4,99 US-Dollar empfehlen, ein einzigartiges Analogon zu Einstiegsservern, das wir für Sie erfunden haben: Die ganze Wahrheit über VPS (KVM) E5-2697 v3 (6 Kerne) 10 GB DDR4 480 GB SSD 1 Gbit / s ab 19 Dollar oder wie teilt man den Server? (Optionen sind mit RAID1 und RAID10, bis zu 24 Kernen und bis zu 40 GB DDR4 verfügbar).

Dell R730xd 2-mal billiger im Equinix Tier IV-Rechenzentrum in Amsterdam? Nur wir haben 2 x Intel TetraDeca-Core Xeon 2 x E5-2697v3 2,6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbit / s 100 TV ab 199 US-Dollar in den Niederlanden! Dell R420 - 2x E5-2430 2,2 GHz 6C 128 GB DDR3 2x960 GB SSD 1 Gbit / s 100 TB - ab 99 US-Dollar! Lesen Sie mehr über das Erstellen von Infrastruktur-Bldg. Klasse mit Dell R730xd E5-2650 v4 Servern für 9.000 Euro für einen Cent?

Source: https://habr.com/ru/post/de484586/


All Articles