IPv6 használata PHP-ben

Nemrég megkaptuk az LIR státuszt és egy /29 IPv6 blokkot. Ezt követően szükségessé vált a kijelölt alhálózatok nyilvántartásának vezetése. Mivel a billing rendszerünk PHP-ben íródott, kicsit jobban el kellett mélyednünk a kérdésben, és rájöttünk, hogy ez a nyelv nem a legbarátságosabb az IPv6 kezelésében. Az alábbiakban bemutatjuk a címekkel és tartományokkal kapcsolatos feladatok megoldását. Lehet, hogy nem a legkifinomultabb, de a kitűzött célokat teljesíti.

IPv6 használata PHP-ben

Egy kis elmélet

Nyilatkozat. Ha ismeri az IPv6-ot és a vele kapcsolatos ismereteket, ez a rész talán unalmas lehet az Ön számára. Talán nem is.

Az embereket, akik először találkoznak az IPv6 notációval, meglepetés érheti. A kifinomult 64.233.177.101 mellett hirtelen találkozunk a 2607:f8b0:4002:c08::8b címkével, és elbizonytalanodhatunk. Az előbbi és az utóbbi is csupán ember által könnyen olvasható 32 és 128 bit értéket képvisel. Minden IP csomag egy olyan fejlécet tartalmaz, amelyben a bitek pontosan standardizált célt szolgálnak. Anélkül, hogy mélyebben belemernénk a fejléc struktúrájába, fontos megértenünk, hogy az IP címekkel és tartományokkal végzett műveletekhez leginkább a bináris matematikát és bitműveleteket célszerű alkalmazni. A legjobban a következő formában célszerű tárolni őket: BINARY(4) IPv4 esetén és BINARY(16) IPv6 esetén.

Egy másik fontos aspektus, amelyet érdemes lenne érinteni, a hálózati maszkok és a CIDR notáció. A CIDR az osztály nélküli köztes irányítást jelenti (békértes adreszálás). Ez a koncepció váltotta fel az osztályozott megközelítést a kérdésben, hogy az IP cím mely része a hálózati prefix, és melyik a hálózati interfész címe ezen a hálózaton belül. A gyakorlatban az első n bit, amelyek a prefixet képviselik, 1 értéket kapnak, a maradék 0-t.

Ember által olvasható formában ez így van megadva: ip.add.re.ss/cidr. Például, 64.233.177.0/24 jelzi, hogy az első 24 bit a prefixhez tartozik. Az utolsó 8 bit, vagyis az ember által olvasható cím végső száma, a szubneten belüli címhez tartozik. Néhány további példát nézzünk. 64.233.177.101/32 és 2607:f8b0:4002:c08::8b/128 — egy konkrét cím. 2607:f8b0:4002:c08::/64 — az első 64 bit (az első 4 csoport) a prefix, a következő 64 bit pedig a lokális rész. Érdekesség, hogy ha valakit zavar a "::" a notációban, a dupla kettőspont tetszőleges számú zérót tartalmazó szekciót helyettesít. Ez a notációban csak egyszer fordulhat elő. Más szavakkal, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.

Mit mit kell ebből az egészből kiemelnünk? Először is, az alhálózat első és utolsó címe a bináris AND és OR segítségével érhető el, ha ismerjük a maszkot bináris formában. Másodszor, a következő alhálózat méretét (azaz a CIDR) n úgy számíthatjuk ki, hogy 1-et adunk hozzá a n-dik pozícióhoz a bináris reprezentációban. A bináris formán azokat az eredményeket értem, amelyeket a következő funkciók segítségével kapunk: pack() és inet_pton() és a következő használatával: bitwise operators, a bináris alatt pedig azt a bináris rendszert értem, amelyet például a következő funkcióval kaphatunk: base_convert().

Történelmi háttérA klasszifikált címzés előtt már az osztály alapú szegmentáció is létezett. Azokban a régi időkben senki sem gondolta, hogy ilyen sok alhálózat lesz, nagy blokkokban osztogatni kezdték őket jobbra-balra: A osztály - a prefix az első 8 bit volt (azaz az első szám), vezető bitje 0; B osztály - az első 16 (az első két szám), vezető bitek 10; C osztály - az első 24 bit, vezető bitek 110. Ezek a vezető bitek határozták meg az osztályba tartozó címek kibocsátási tartományait: 0.0.0.0 — 127.255.255.255 A osztályhoz, 128.0.0.0 — 191.255.255.255 — B osztály, 192.0.0.0 — 223.255.255.255 — C osztály. Ahogy az internet terjedt a világban, a szabályozók rájöttek, hogy hibáztak, és az 1990-es évek elején kidolgozták a klasszifikálatlan koncepciót, amely lehetővé tette, hogy ne ragaszkodjanak a vezető bitekhez. Egy kicsit több részletet találhatunk például a nagy és mindenható.

Térjünk át a gyakorlatra

A gyakorlatban három legvalószínűbb feladatot valósítunk meg, úgy gondolom:

  1. az első és az utolsó cím megkapása a tartományban;
  2. a megadott méretű következő tartomány megszerzése (CIDR);
  3. a cím tartományhoz való tartozásának ellenőrzése.

A megvalósítás IPv6-ra vonatkozik, de ha szükséges, a logika könnyen alkalmazható. Néhány ötletet érdekében jelentett, de kissé másképp valósítottam meg. Az példákban nincs bemeneti hibaellenőrzés. Tehát lássunk neki. innenAhogyan említettem, az első és utolsó cím a tartományban bitműveletek segítségével határozható meg, ha tudjuk a tartomány elejét és a bináris alhálózati maszkot. Ennek megfelelően első lépésként a CIDR-t bináris maszkká kell alakítanunk. Ehhez összegyűjtjük hexadecimális reprezentációját és binárisra csomagoljuk.

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); }

pack(‘H*’, $mask)

Hívás a hexadecimális reprezentációt ugyanúgy csomagolja, mint a . Az egyetlen különbség az, hogy a híváskor inet_pton(). Csak annyival különbözik, hogy a hívás során pack() Minden 0-nak a helyén kell lennie, és a feljegyzésben kettőspontnak nem szabad lennie, ellentétben az ember által érthető feljegyzéssel.

A következő lépés a tartomány elejének és végének kiszámítása. Itt jelentkeznek a bonyodalmak. A bitműveletek a processzor bitméretére korlátozódnak. Tehát a 32 bites CubieTruck-on, amelyet néha különféle tesztelési célokra használok, nem lehet a teljes 128 bit címét egy műveletben feldolgozni. Azonban semmi sem akadályoz meg abban, hogy 32 bites csoportokra bontsuk (elővigyázatosságból, ki tudja, milyen processzorokon fogunk futni).

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);
    }
}

Jövőbeni felhasználásra tervezzük a lehetőséget, hogy IP-t adjunk át, és az eredményt bináris és ember által olvasható formában kapjuk meg. A paraméter $which itt azt határozza meg, hogy a tartomány elejét vagy végét szeretnénk-e kapni (előtagok 'start' vagy 'end' vagyis).

A következő feladat (ráadásul a legpraktikusabb a cégünk számára) a következő tartomány kiszámítása. Erre a feladatra nem jutott jobb eszembe, mint a címet bináris formába bontani, és 1-et hozzáadni a szükséges pozícióban, majd mindent visszaállítani. Annak érdekében, hogy ne keletkezzenek artefaktumok, a cím bontásakor és összerakásakor bájtonként akartam dolgozni.

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);
    }
}

A kimenetben megkapjuk a következő tartomány prefixét, amelynek méretét a megadott $cidr. Ezt a funkciót használjuk a címblokkok ügyfeleinknek való kiemelésére.

Végül ellenőrizzük, hogy a cím a tartományban van-e. Például egy /48-as blokkot foglaltunk le az ügyfeleknek /64-es blokkok kiosztására, és meg kell győződnünk arról, hogy a kiosztás során nem lépjük túl a fenntartott blokk határait (a gyakorlatban ez nem fog hamar bekövetkezni, de a lehetőség mindig fennáll). Itt minden egyszerű. Megkapjuk a tartomány elejét és végét bináris formában, és ellenőrizzük, hogy a cím a határokon belül van-e.

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);
}

Remélem, hasznos volt. Milyen más címkezelő funkciókat tartana hasznosnak? Bármilyen kiegészítést, megjegyzést és kód-értékelést szívesen látok a hozzászólásokban.

Ha Ön már ügyfelünk, vagy csak fontolgatja, hogy azzá váljon, az e cikk megjelenése alkalmából felajánljuk, hogy ingyen kaphat egy /64 blokkot minden VPS vagy dedikált szerver szolgáltatásunkhoz az Equinix Tier IV adatközpontban, Hollandiában, árajánlatkérő jegy benyújtásával, amelyben megadja ezt a cikket. Az ajánlat 2020 márciusáig érvényes.

Egy kis reklám 🙂

Köszönjük, hogy velünk maradtál. Tetszenek a cikkeink? Szeretnél több érdekes anyagot látni? Támogass minket, és rendelés leadásával vagy ismerősöknek ajánlással segíts nekünk. Felhőalapú VPS fejlesztőknek 4,99 dollártól, Egyedülálló alternatívája az entry-level szervereknek, amelyet Önnek találtunk ki: Minden igazság a VPS (KVM) E5-2697 v3 (6 mag) 10GB DDR4 480GB SSD 1Gbps áron 19 dollártól, vagy hogyan oszd fel helyesen a szervert? (elérhetők RAID1 és RAID10 opciók, akár 24 magig és 40GB DDR4-ig).

Dell R730xd kétszer olcsóbban a Tier IV Equinix adatközpontban Amszterdamban? Csak nálunk 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TB 199 dollártól Hollandiában! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — 99 dollártól! Olvasd el ezt Hogyan építsünk vállalati szintű infrastruktúrát Dell R730xd E5-2650 v4 szerverekkel, amelyek költsége 9000 euróban van megszabva?

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster