Puna me IPv6 në PHP

Së fundmi kemi marrë statusin LIR dhe bllokun /29 IPv6. Dhe më pas lindi nevoja për të mbajtur gjurmët e nënrrjetave të caktuara. Dhe meqenëse faturimi ynë është i shkruar në PHP, na u desh të thelloheshim pak në këtë çështje dhe të kuptonim se kjo gjuhë nuk është më miqësorja për sa i përket punës me IPv6. Më poshtë prerjes është zgjidhja jonë për problemet që lindin kur punoni me adresat dhe diapazonin. Ndoshta jo më elegantja, por e bën punën.

Puna me IPv6 në PHP

Pak teori

Mohim përgjegjësie. Nëse jeni njohur me atë që është IPv6 dhe çfarë vjen me të, kjo pjesë mund të jetë e mërzitshme për ju. Mund të mos jetë.

Njerëzit që shohin shënimin IPv6 për herë të parë mund ta shohin atë mjaft të frikshëm. Pas elegante 64.233.177.101 jemi përballur papritur me të 2607:f8b0:4002:c08::8b dhe mund të ngatërrohemi. Të dyja janë vetëm paraqitje të lexueshme nga njeriu, përkatësisht 32 dhe 128 bit. Çdo paketë IP përmban një kokë me një qëllim rreptësisht të standardizuar për çdo bit. Pa u futur më thellë në strukturën e titujve, një gjë që duhet të heqim nga kjo është se për operacionet me adresa IP dhe intervale, përgjithësisht është e përshtatshme të përdoret matematika binare dhe operacionet bit. Është gjithashtu më i përshtatshëm për t'i ruajtur ato në bazën e të dhënave si BINARI (4) për IPv4 dhe BINARI (16) për IPv6.

Një aspekt tjetër i rëndësishëm që ia vlen të preket janë maskat e rrjetit dhe shënimi CIDR. CIDR është një akronim për Rrugëtimin pa klasa midis domeneve (adresim pa klasë). Ky koncept ka zëvendësuar konceptin e klasës në përcaktimin se cila pjesë e adresës IP është prefiksi i rrjetit dhe cila pjesë është adresa e ndërfaqes së rrjetit brenda këtij rrjeti. Në praktikë, n bitet e para që korrespondojnë me prefiksin do të vendosen në 1, pjesa tjetër në 0.

Në formë të kuptueshme njerëzore, kjo shkruhet në formë ip.add.re.ss/cidr. Për shembull, 64.233.177.0/24 tregon se 24 bitet e para i referohen prefiksit. 8 bitet e fundit, të njohur edhe si numri i fundit në shënimin e lexueshëm nga njeriu, i referohen adresës brenda nënrrjetit. Disa ushtrime të tjera. 64.233.177.101/32 и 2607:f8b0:4002:c08::8b/128 - një adresë specifike. 2607:f8b0:4002:c08::/64 - 64 bitet e para (4 grupet e para) jane prefiksi, 64 bitet e mbetura jane pjesa lokale. Meqë ra fjala, nëse dikush është i hutuar nga "::" në hyrje, dy pika zëvendëson një numër arbitrar seksionesh që përmbajnë 0. Mund të shfaqet vetëm një herë në shënim. Me fjale te tjera, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.

Çfarë duhet të mësojmë nga e gjithë kjo? Së pari, adresa e parë dhe e fundit e nënrrjetit mund të merret duke përdorur binare AND dhe OR, duke ditur maskën në formë binare. Së dyti, madhësia e nënrrjetit tjetër (d.m.th. me CIDR) n mund të llogaritet duke shtuar 1 në n-ai pozicion në paraqitjen binar. Me binar nënkuptoj rezultatin e përdorimit të funksioneve paketë () и inet_pton() dhe përdorim të mëtejshëm operatorët bitwise, me binar është përfaqësimi i sistemit binar që mund të merret, të themi, bazë_konvert().

Informacioni historikAdresimi pa klasa u parapri nga ndarja e klasave. Në ato vite të largëta, askush nuk e imagjinonte se do të kishte kaq shumë nënrrjeta; ato u shpërndanë majtas dhe djathtas në blloqe të mëdha: klasa A - parashtesa ishte 8 bitet e para (d.m.th. numri i parë), me një bit kryesor 0; klasa B - 16-të e parë (dy numrat e parë), bitet kryesore 10; Klasa C - 24 bitet e para, bitet kryesore 110. Këta bit kryesorë specifikuan intervalet në të cilat u lëshua adresa e një klase të caktuar: 0.0.0.0 - 127.255.255.255 për klasën A, 128.0.0.0 - 191.255.255.255 - klasa B, 192.0.0.0 - 223.255.255.255 - klasa C. Ndërsa interneti u përhap në të gjithë planetin, rregullatorët kuptuan se e kishin humbur pikën dhe në fillim të viteve '90 ata zhvilluan një koncept pa klasë që bëri të mundur që të mos ishin të lidhur te pjesët kryesore. Pak më shumë detaje mund të gjenden në, të themi, i madh dhe i gjithëdijshëm.

Le të kalojmë në praktikë

Në praktikë, ne do të zbatojmë tre detyrat më të mundshme, siç më dukej,:

  1. marrja e adresës së parë dhe të fundit të gamës;
  2. merrni gamën tjetër të madhësisë së dhënë (CIDR);
  3. duke kontrolluar nëse një adresë i përket një diapazoni.

Zbatimi do të jetë për IPv6, por nëse është e nevojshme, logjika mund të përshtatet lehtësisht. Kam disa ide prandaj, por e zbatoi pak më ndryshe. Gjithashtu, shembujt nuk kontrollojnë për gabime në hyrje. Pra, le të shkojmë.

Siç e përmenda tashmë, adresa e parë dhe e fundit e një diapazoni mund të përcaktohet duke përdorur operacione bitwise, duke ditur fillimin e diapazonit dhe maskën binare të nënrrjetit. Prandaj, gjëja e parë që duhet të bëjmë është ta kthejmë CIDR në një maskë binare. Për ta bërë këtë, ne do të mbledhim paraqitjen e tij gjashtëkëndore dhe do ta paketojmë në një binar.

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

Вызов paketë ('H*', $mask) paketon paraqitjen gjashtëkëndore në të njëjtën mënyrë si inet_pton(). I vetmi ndryshim është se kur telefononi paketë () të gjitha 0-të duhet të jenë në vendet e tyre dhe nuk duhet të ketë dy pika në hyrje, ndryshe nga hyrja e lexueshme nga njeriu.

Hapi tjetër është llogaritja e fillimit dhe mbarimit të diapazonit. Dhe këtu lindin nuanca. Operacionet në bit janë të kufizuara nga kapaciteti bit i procesorit. Prandaj, në CubieTruck tim 32-bit, të cilin ndonjëherë e përdor për të gjitha llojet e përkëdheljeve testuese, nuk do të jetë e mundur të përpunohen të gjitha 128 bit-et e adresës në një operacion. Sidoqoftë, asgjë nuk na pengon ta ndajmë atë në grupe prej 32 bitësh (për çdo rast, kush e di se me çfarë procesorë do të punojmë).

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

Për përdorim në të ardhmen, ne do të ofrojmë mundësinë për të transmetuar IP dhe për të marrë rezultatin si në formë binare ashtu edhe në formë të lexueshme nga njeriu. Parametri $cila këtu ai specifikon nëse duam të marrim fillimin ose fundin e diapazonit (vlerat 'fillo' ose 'fundi' përkatësisht).

Detyra tjetër (dhe gjithashtu më praktike për kompaninë tonë) është llogaritja e diapazonit tjetër. Për këtë detyrë, asgjë më e mirë nuk erdhi në mendje sesa të zgjerosh adresën në një varg binar dhe të shtosh 1 në pozicionin e dëshiruar, dhe më pas të rrëzosh gjithçka përsëri. Për të shmangur shfaqjen e objekteve kudo, vendosa të ndaja adresën për bajt gjatë dekompozimit dhe montimit.

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

Në dalje marrim prefiksin e diapazonit të madhësisë tjetër të specifikuar në $cidr. Me këtë funksion ne ndajmë blloqe adresash për klientët tanë.

Më në fund, kontrolloni nëse adresa i përket diapazonit. Për shembull, ne kemi ndarë një bllok /48 për shpërndarjen e blloqeve te klientët /64, dhe duhet të sigurohemi që gjatë caktimit të mos shkojmë përtej bllokut të caktuar (në praktikë kjo nuk do të ndodhë së shpejti, por ka ende një mundësi). Gjithçka është e thjeshtë këtu. Ne marrim fillimin dhe fundin e diapazonit në formë binare dhe kontrollojmë nëse adresa është brenda kufijve.

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

Shpresoj se kjo ishte e dobishme. Cilat funksione të tjera për të punuar me adresat mund të jenë të dobishme sipas mendimit tuaj? Çdo shtesë, koment dhe rishikim i kodit është i mirëpritur ngrohtësisht në komente.

Nëse tashmë jeni klienti ynë ose thjesht po mendoni të bëheni një, me rastin e publikimit të këtij artikulli ju ofrojmë të merrni një bllok /64 plotësisht falas për të gjitha shërbimet vps ose serverin e dedikuar në qendrën e të dhënave Equinix Tier IV, Holandë me kërkesë të departamentit të shitjeve duke ofruar një lidhje me këtë artikull në biletë. Oferta është e vlefshme deri në Mars 2020.

Disa reklama 🙂

Faleminderit që qëndruat me ne. A ju pëlqejnë artikujt tanë? Dëshironi të shihni përmbajtje më interesante? Na mbështesni duke bërë një porosi ose duke rekomanduar miqve, cloud VPS për zhvilluesit nga 4.99 dollarë, një analog unik i serverëve të nivelit të hyrjes, i cili u shpik nga ne për ju: E gjithë e vërteta rreth VPS (KVM) E5-2697 v3 (6 bërthama) 10 GB DDR4 480 GB SSD 1 Gbps nga 19 dollarë ose si të ndani një server? (e disponueshme me RAID1 dhe RAID10, deri në 24 bërthama dhe deri në 40 GB DDR4).

Dell R730xd 2 herë më lirë në qendrën e të dhënave Equinix Tier IV në Amsterdam? Vetëm këtu 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV nga 199$ në Holandë! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - nga 99 dollarë! Lexoni rreth Si të ndërtohet korporata e infrastrukturës. klasë me përdorimin e serverëve Dell R730xd E5-2650 v4 me vlerë 9000 euro për një qindarkë?

Burimi: www.habr.com

Bleni një host të besueshëm për faqet me mbrojtje DDoS, serverë VPS VDS 🔥 Bleni hosting të besueshëm të faqeve të internetit me mbrojtje DDoS, servera VPS VDS | ProHoster