Töötamine IPv6-ga PHP-s

Hiljuti saime LIR staatuse ja /29 IPv6 ploki. Seejärel tekkis vajadus arvestada määratud alateid. Kuna meie arveldus on kirjutatud PHP-s, tuli natuke süveneda sellesse küsimusse ja mõista, et see keel ei ole IPv6 osas kõige sõbralikum. Allpool on meie lahendus tõusetunud probleemidele, mis on seotud aadresside ja vahemikega. Võib-olla mitte kõige elegantsem, kuid seatud eesmärgid on saavutatud.

Töötamine IPv6-ga PHP-s

Natuke teooriat

Distsipliin. Kui olete tuttav IPv6 kontseptsiooniga ja teate, milleks seda kasutatakse, võib see osa teile igav olla. Võib ka mitte olla.

Inimesed, kes näevad IPv6 annotatsiooni esmakordselt, võivad sellest kergesti segadusse minna. Pärast elegantseid 64.233.177.101 kohtume ootamatult 2607:f8b0:4002:c08::8b ja võime erutuda. Mõlemad on vaid inimesele arusaadavad kujutised vastavalt 32 ja 128 bitist. Iga IP-pakett sisaldab päist, millel on rangelt standarditud iga bitide määratlemine. Ilma päiste struktuuri sügavamale laskumata peaksime siit välja tooma ühe: IP-aadresside ja vahemike operatsioonide jaoks on mõistlik kasutada peamiselt binaarset matemaatikat ja bititasemel operatsioone. Neid on ka andmebaasis mugavam hoida kui BINAARNE(4) IPv4 jaoks ja BINAARNE(16) IPv6 jaoks.

Veel üks oluline aspekt, mida tasub käsitleda, on võrku maskid ja CIDR notatsioon. CIDR on akronüüm sõnadest Classless Inter-Domain Routing (klassifitseerimata adresseerimine). See kontseptsioon tuli klassikalise adresseerimise asemele, et määrata, milline osa IP-aadressist on võrgu prefiks ja milline on liidese aadress selle võrgu sees. Praktikas on esimesed n bitti, mis vastavad prefiksile, seadistatud väärtuseks 1, teised bitti saavad väärtuseks 0.

Inimesele arusaadavas vormis kirjutatakse see kujul ip.add.re.ss/cidr. Näiteks, 64.233.177.0/24 60.24.0.0/24 tähendab, et esimesed 24 bitti kuuluvad prefiksile. Viimased 8 bitti, mis on samuti viimane number inimkeeles arusaadavas kirjutises, viitavad aadressile alamvõrgus. Veel paar harjutust. 64.233.177.101/32 ja 2607:f8b0:4002:c08::8b/128 — üks konkreetne aadress. 2607:f8b0:4002:c08::/64 — esimesed 64 bitti (esimesed 4 gruppi) on prefiks, ülejäänud 64 bitti on lokaalosa. Muide, kui kedagi häirib "::" kirjutises, siis kahekordne koolon asendab suvalise arvu sektsioone, mis sisaldavad 0. Seda võib esineda annotatsioonis ainult 1 kord. Teisisõnu, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.

Mida me sellest kõikjast järeldame? Esiteks, alamsaadete esimese ja viimase aadressi saab määrata kasutades binaarseid AND ja OR, teades maski binaarses vormis. Teiseks, järgmist alamsaadet suurusega (st CIDR) n saab arvutada, lisades 1 n-ndale positsioonile binaarsetes esitustes. Binaarse all mõistan ma funktsioonide pack() ja inet_pton() ja edasist kasutamist bitipõhiste operaatorite, binaarsetena — esitus binaarses süsteemis, mille saab näiteks saada funktsiooni kaudu base_convert().

Ajalooline ülevaadeKlassituste eelõht eelnes klassile segmenteeritud adresseerimine. Tollal ei osanud keegi arvata, et alamsateid tuleb nii palju, neid jagati laialdaselt suurte plokkidena: klass A — eelmise 8 bitti (st esimene number), esimesel bitil 0; klass B — esimesed 16 bitti (esimesed kaks numbrit), esimesed bitid 10; klass C — esimesed 24 bitti, esimesed bitid 110. Need juhtivad bitid määrasid vahemikud, kus anti välja aadress vastava klassi jaoks: 0.0.0.0 — 127.255.255.255 klass A, 128.0.0.0 — 191.255.255.255 — klass B, 192.0.0.0 — 223.255.255.255 — klass C. Kui internet hakkas üle maailma laienema, mõistsid reguleerijad, et nad olid eksinud, ning 90ndate alguses töötati välja klassitu kontseptsioon, mis võimaldas mitte siduda end juhtivate bittidega. Veidi rohkem üksikasju saab leida näiteks suurest ja kõiketeadvast.

Liigume praktika juurde

Praktikas rakendame kolme kõige tõenäolisemat, nagu mulle tundub, ülesannet:

  1. esimese ja viimase aadressi saamine vahemikus;
  2. järgmise antud suurusega vahemiku saamine (CIDR);
  3. aadressi kuuluvuse kontrollimine vahemikku.

Rakendus on IPv6 jaoks, kuid vajadusel saab loogikat hõlpsasti kohandada. Mõned ideed olen saanud siit, kuid rakendasin neid veidi teistsuguselt. Samuti ei sisalda näidetes sisendi vigade kontrollimist. Nii et lähme edasi.

Nagu juba mainisin, saab vahemiku esimese ja viimase aadressi määrata bititasemeliste operatsioonide abil, teades vahemiku algust ja binaarset alamhulga maski. Seetõttu peame esmalt CIDRi binaarseks maskiks muutma. Selleks kogume selle hex-esituse ja pakime selle binaarsesse formaati.

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

Kutse pack('H*', $mask) pakendatakse hex esitus samamoodi nagu inet_pton(). Ainult et kutsumise korral pack() peavad kõik 0 olema oma kohtadel ning koolonid esituses ei tohi olla, erinevalt inimloetavast esitlusest.

Järgmine samm on vahemiku alguse ja lõpu arvutamine. Siin tulevad mängu nüansid. Bitilised operatsioonid on piiratud protsessori bitiga. Seega, minu 32-bitises CubieTruckis, mida ma mõnikord kasutan igasuguste testide jaoks, ei saa kõiki 128 bitti aadressist korraga töödelda. Siiski ei takista meid midagi jagamast seda 32-bitiseks rühmaks (igaks juhuks, keegi teab, millistel protsessoritel me jooksma hakkame).

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

Tulevaseks kasutamiseks planeerime võimaluse edastada IP ja saada tulemus nii binaarselt kui ka inimloetaval kujul. Parameeter $which määrab, kas tahame saada vahemiku algust või lõppu (väärtused 'start' või 'end' vastavalt).

Järgmine ülesanne (ja ka kõige praktilisem meie ettevõtte jaoks) on järgmise vahemiku arvutamine. Selle ülesande jaoks ei tulnud midagi paremat kui adresseerimise lahutamine binaarstringiks ja 1 lisamine vajalikes kohtades, seejärel kõik tagasi kokku panna. Et mitte jätta jälgi, otsustasin adresseerimise lahutamisel ja kogumisel adresseerimist byte-ks jagada.

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

Väljundina saame järgmise vahemiku eesliite, mille suurus on märgitud $cidr. Selle funktsiooniga anname aadressibloke oma klientidele.

Lõpuks kontrollime, kas aadress kuulub vahemikku. Näiteks, oleme eraldanud ühe bloki /48 klientidele /64 bloki jagamiseks ning peame veenduma, et määramisel ei ületa me eraldatud bloki piire (praktikas see ei juhtu varsti, kuid siiski on tõenäosus olemas). Siin on kõik lihtne. Saame vahemiku alguse ja lõpu binaarses vormis ning kontrollime, kas aadress jääb vahemikku.

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

Loodan, et see oli kasulik. Millised muud aadresside haldamise funktsioonid võiksid Teie arvates kasulikud olla? Igaüht, kes lisab kommentaare, täiendusi või koodivaatlust, on oodatud kommentaarides.

Kui Te olete juba meie klient või alles kaalute selleks saamist, siis seoses selle artikli ilmumisega pakume Teile võimalust saada tasuta /64 plokk kõigi VPS või dedikeeritud serverite teenuste jaoks Equinix Tier IV andmekeskuses, Hollandis, müügiosakonnale vestlusesse viidates sellele artiklile. Pakkumine kehtib kuni märtsi 2020.

Veidi reklaami 🙂

Aitäh, et olete meiega. Kas teile meeldivad meie artiklid? Kas soovite näha rohkem huvitavaid materjale? Toetage meid, tehes tellimuse või soovitades meid tuttavatele. pilve VPS arendajatele alates $4,99, ainulaadne sisenemise taseme serverite analoog, mille oleme teie jaoks välja mõelnud: Kogu tõde VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps alates $19 või kuidas õieti serverit jagada? (saadaval on RAID1 ja RAID10 variandid, kuni 24 südamikku ja kuni 40GB DDR4).

Kas Dell R730xd on kaks korda odavam Equinixi Tier IV andmete keskuses Amsterdamis? Ainult meie juures 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TB alates $199 Hollandi turul! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — alates $99! Lugege, kuidas Luua ettevõtte tasemel infrastruktuur Dell R730xd E5-2650 v4 serveritega, mille hind on 9000 eurot, madala hinnaga?

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster