Práca s IPv6 v PHP

Nedávno sme dostali stav LIR a /29 blokovanie IPv6. A potom vznikla potreba sledovať pridelené podsiete. A keďže naše účtovanie je napísané v PHP, museli sme sa do problematiky trochu ponoriť a uvedomiť si, že tento jazyk nie je práve najprívetivejší z hľadiska práce s IPv6. Pod rezom je naše riešenie problémov, ktoré vznikajú pri práci s adresami a rozsahmi. Možno nie najelegantnejší, ale robí svoju prácu.

Práca s IPv6 v PHP

Niektoré teórie

Vylúčenie zodpovednosti. Ak ste oboznámení s tým, čo je IPv6 a s čím prichádza, táto časť môže byť pre vás nudná. Možno nie.

Ľudia, ktorí vidia anotáciu IPv6 prvýkrát, to môže zdať dosť skľučujúce. Po elegantnom 64.233.177.101 zrazu čelíme 2607:f8b0:4002:c08::8b a môžeme byť zmätení. Obidve sú len ľudsky čitateľné reprezentácie 32 a 128 bitov. Každý IP paket obsahuje hlavičku s prísne štandardizovaným účelom pre každý bit. Bez toho, aby sme zachádzali hlbšie do štruktúry hlavičiek, jednu vec, ktorú si z toho musíme odniesť, je, že pre operácie s IP adresami a rozsahmi je vo všeobecnosti vhodné použiť binárnu matematiku a bitové operácie. Najvýhodnejšie je tiež uložiť ich do databázy ako BINÁRNE (4) pre IPv4 a BINÁRNE (16) pre IPv6.

Ďalším dôležitým aspektom, ktorého sa oplatí dotknúť, sú masky siete a zápis CIDR. CIDR je skratka pre beztriedne smerovanie medzi doménami (beztriedne oslovovanie). Tento koncept nahradil koncept triedy pri určovaní, ktorá časť IP adresy je predponou siete a ktorá časť je adresou sieťového rozhrania v rámci tejto siete. V praxi sa prvých n bitov zodpovedajúcich prefixu nastaví na 1, zvyšok na 0.

V ľudsky zrozumiteľnej forme je to napísané vo forme ip.add.re.ss/cidr, Napríklad 64.233.177.0/24 označuje, že prvých 24 bitov sa vzťahuje na predponu. Posledných 8 bitov, známych aj ako posledné číslo v ľudsky čitateľnom zápise, odkazuje na adresu v rámci podsiete. Ešte pár cvikov. 64.233.177.101/32 и 2607:f8b0:4002:c08::8b/128 - jedna konkrétna adresa. 2607:f8b0:4002:c08::/64 - prvých 64 bitov (prvé 4 skupiny) je prefix, zvyšných 64 bitov je lokálna časť. Mimochodom, ak je niekto zmätený „::“ v zázname, dvojitá dvojbodka nahradí ľubovoľný počet sekcií obsahujúcich 0. V anotácii sa môže objaviť iba raz. Inými slovami, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.

Čo sa z toho všetkého musíme naučiť? Po prvé, prvú a poslednú adresu podsiete je možné získať pomocou binárnych operátorov AND a OR, ak poznáme masku v binárnej forme. Po druhé, veľkosť ďalšej podsiete (t. j. s CIDR) n možno vypočítať pripočítaním 1 k n-tá pozícia v binárnom zobrazení. Binárnym mám na mysli výsledok používania funkcií balenie() и inet_pton() a ďalšie použitie bitové operátory, binárne je reprezentácia binárneho systému, ktorú možno získať, povedzme, base_convert().

Historické informácieBeztriednemu oslovovaniu predchádzala triedna segregácia. V tých vzdialených rokoch si nikto nepredstavoval, že bude toľko podsietí, boli rozdelené vľavo a vpravo vo veľkých blokoch: trieda A - predpona bola prvých 8 bitov (t. j. prvé číslo), s počiatočným bitom 0; trieda B - prvých 16 (prvé dve čísla), prvé bity 10; trieda C - prvých 24 bitov, úvodné bity 110. Tieto úvodné bity špecifikovali rozsahy, v ktorých bola vydaná adresa konkrétnej triedy: 0.0.0.0 - 127.255.255.255 pre triedu A, 128.0.0.0 - 191.255.255.255 - trieda B, 192.0.0.0 - 223.255.255.255 - trieda C. Ako sa internet šíril po celej planéte, regulátori si uvedomili, že minuli cieľ, a na začiatku 90. rokov vyvinuli beztriedny koncept, ktorý umožnil neviazať sa k vedúcim bitom. Trochu viac podrobností nájdete napr. skvelý a vševediaci.

Prejdime k praxi

V praxi implementujeme tri najpravdepodobnejšie, ako sa mi zdalo, úlohy:

  1. získanie prvej a poslednej adresy rozsahu;
  2. získať ďalší daný rozsah veľkostí (CIDR);
  3. kontrola, či adresa patrí do rozsahu.

Implementácia bude pre IPv6, no v prípade potreby sa dá logika jednoducho prispôsobiť. Dostal som nejaké nápady preto, ale implementovali ho trochu inak. Príklady tiež nekontrolujú chyby vstupu. Tak, poďme.

Ako som už spomenul, prvú a poslednú adresu rozsahu je možné určiť pomocou bitových operácií, pričom poznáme začiatok rozsahu a binárnu masku podsiete. Preto prvá vec, ktorú musíme urobiť, je zmeniť CIDR na binárnu masku. Aby sme to dosiahli, zhromaždíme jeho hexadecimálne zobrazenie a zabalíme ho do binárneho.

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

volanie pack('H*', $mask) zabalí hexadecimálnu reprezentáciu rovnakým spôsobom ako inet_pton(). Rozdiel je len v tom, že pri volaní balenie() všetky 0 musia byť na svojich miestach a v položke by nemali byť žiadne dvojbodky, na rozdiel od položky čitateľnej pre človeka.

Ďalším krokom je výpočet začiatku a konca rozsahu. A tu vznikajú nuansy. Bitové operácie sú obmedzené bitovou kapacitou procesora. V súlade s tým na mojom 32-bitovom CubieTrucku, ktorý niekedy používam na všetky druhy testovacieho rozmaznávania, nebude možné spracovať všetkých 128 bitov adresy v jednej operácii. Nič nám však nebráni rozdeliť si ho do skupín po 32 bitoch (pre každý prípad, ktovie, na akých procesoroch budeme bežať).

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

Pre budúce použitie poskytneme možnosť prenášať IP a prijímať výsledok v binárnej aj pre človeka čitateľnej forme. Parameter $ ktorý tu určuje, či chceme získať začiatok alebo koniec rozsahu (hodnoty 'start' alebo 'koniec' v uvedenom poradí).

Ďalšou úlohou (a zároveň najpraktickejšou pre našu spoločnosť) je výpočet ďalšieho rozsahu. Pre túto úlohu ma nenapadlo nič lepšie, ako rozbaliť adresu do binárneho reťazca a pridať 1 na požadovanú pozíciu a potom všetko zbaliť späť. Aby sa nikde neobjavili artefakty, rozhodol som sa pri rozklade a zostavovaní rozdeliť adresu podľa bajtov.

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

Na výstupe dostaneme predponu ďalšieho rozsahu veľkostí špecifikovaného v $cidr. Pomocou tejto funkcie prideľujeme bloky adries našim klientom.

Nakoniec skontrolujte, či adresa patrí do rozsahu. Napríklad sme vyčlenili jeden blok /48 na distribúciu blokov klientom /64, a treba si dať pozor, aby sme pri prideľovaní neprekročili pridelený blok (v praxi sa to tak skoro nestane, ale stále je tu možnosť). Všetko je tu jednoduché. Dostaneme začiatok a koniec rozsahu v binárnom tvare a skontrolujeme, či je adresa v medziach.

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

Dúfam, že to bolo užitočné. Aké ďalšie funkcie na prácu s adresami môžu byť podľa vás užitočné? Akékoľvek dodatky, komentáre a recenzie kódu sú srdečne vítané v komentároch.

Ak už ste našim klientom alebo len uvažujete o tom, že sa ním stanete, pri príležitosti uverejnenia tohto článku vám ponúkame získanie bločku /64 úplne zadarmo pre všetky služby vps alebo dedikovaný server v dátovom centre Equinix Tier IV v Holandsku na požiadanie obchodnému oddeleniu poskytnutím odkazu na tento článok v tikete. Ponuka platí do marca 2020.

Nejaké inzeráty 🙂

Ďakujeme, že ste zostali s nami. Páčia sa vám naše články? Chcete vidieť viac zaujímavého obsahu? Podporte nás zadaním objednávky alebo odporučením priateľom, cloud VPS pre vývojárov od 4.99 USD, jedinečný analóg serverov základnej úrovne, ktorý sme pre vás vymysleli: Celá pravda o VPS (KVM) E5-2697 v3 (6 jadier) 10GB DDR4 480GB SSD 1Gbps od 19 USD alebo ako zdieľať server? (k dispozícii s RAID1 a RAID10, až 24 jadier a až 40 GB DDR4).

Dell R730xd 2 krát lacnejší v dátovom centre Equinix Tier IV v Amsterdame? Len tu 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbps 100 TV od 199 USD v Holandsku! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gb/s 100 TB – od 99 USD! Čítať o Ako vybudovať infraštruktúru spol. triedy s využitím serverov Dell R730xd E5-2650 v4 v hodnote 9000 XNUMX eur za cent?

Zdroj: hab.com

Kúpte si spoľahlivý hosting pre stránky s DDoS ochranou, VPS VDS servery 🔥 Kúpte si spoľahlivý webhosting s ochranou DDoS, VPS VDS servery | ProHoster