Neseniai gavome LIR būseną ir /29 IPv6 bloką. Ir tada atsirado poreikis sekti priskirtus potinklius. O kadangi mūsų atsiskaitymas surašytas PHP, teko šiek tiek įsigilinti į problemą ir suprasti, kad ši kalba nėra pati draugiškiausia dirbant su IPv6. Žemiau yra mūsų problemų, kylančių dirbant su adresais ir diapazonais, sprendimas. Galbūt ne pats elegantiškiausias, bet jis atlieka savo darbą.

Teorijos tiek
Atsisakymas. Jei žinote, kas yra IPv6 ir su kuo jis pateikiamas, ši dalis jums gali būti nuobodi. Gali ir nebūti.
Žmonės, kurie pirmą kartą mato IPv6 anotaciją, gali atrodyti gana bauginančiai. Po elegantiško 64.233.177.101 staiga susiduriame su 2607:f8b0:4002:c08::8b ir mes galime susipainioti. Abu yra tik žmogaus skaitomi atitinkamai 32 ir 128 bitų atvaizdai. Bet kuriame IP pakete yra antraštė su griežtai standartizuotu kiekvieno bito tikslu. Nesigilindami į antraščių struktūrą, turime atimti vieną dalyką – operacijoms su IP adresais ir diapazonais paprastai patogu naudoti dvejetainę matematiką ir bitines operacijas. Taip pat patogiausia juos saugoti duomenų bazėje kaip DVEJETAS (4) skirtas IPv4 ir DVEJETAS (16) skirtas IPv6.
Kitas svarbus aspektas, kurį verta paliesti, yra tinklo kaukės ir CIDR žymėjimas. CIDR yra beklasio tarpdomeninio maršruto akronimas (). Ši koncepcija pakeitė klasės sąvoką nustatant, kuri IP adreso dalis yra tinklo priešdėlis, o kuri – tinklo sąsajos adresas šiame tinkle. Praktiškai pirmieji n bitų, atitinkančių priešdėlį, bus nustatyti į 1, o kiti - į 0.
Žmogui suprantama forma tai parašyta forma ip.add.re.ss/cidr. Pavyzdžiui, 64.233.177.0/24 rodo, kad pirmieji 24 bitai nurodo priešdėlį. Paskutiniai 8 bitai, taip pat žinomi kaip paskutinis skaičius žmogaus skaitomu užrašu, nurodo adresą potinklyje. Dar pora pratimų. 64.233.177.101/32 и 2607:f8b0:4002:c08::8b/128 - vienas konkretus adresas. 2607:f8b0:4002:c08::/64 - pirmieji 64 bitai (pirmosios 4 grupės) yra priešdėlis, likę 64 bitai yra vietinė dalis. Beje, jei ką nors suklaidina „::“ įraše, dvigubas dvitaškis pakeičia savavališką skaičių sekcijų, kuriose yra 0. Anotacijoje jis gali būti rodomas tik vieną kartą. Kitaip tariant, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.
Ko iš viso to turime pasimokyti? Pirma, pirmąjį ir paskutinį potinklio adresą galima gauti naudojant dvejetainius AND ir ARBA, žinant kaukę dvejetaine forma. Antra, kitas potinklio dydis (t. y. su CIDR) n galima apskaičiuoti pridedant 1 prie n-ta padėtis dvejetainiame vaizde. Dvejetainis turiu galvoje funkcijų naudojimo rezultatą paketas() и inet_pton() ir tolesnis naudojimas , dvejetainis yra dvejetainės sistemos vaizdas, kurį galima gauti, tarkime, base_convert().
Istorinė informacija,Prieš kreipiantis be klasių buvo taikoma klasių atskyrimas. Tais tolimais metais niekas neįsivaizdavo, kad bus tiek daug potinklių, jie buvo paskirstyti į kairę ir į dešinę dideliais blokais: A klasė - priešdėlis buvo pirmieji 8 bitai (t. y. pirmasis skaičius), o pirmas bitas buvo 0; B klasė - pirmieji 16 (pirmi du skaičiai), pirmieji bitai 10; C klasė - pirmieji 24 bitai, pirmieji bitai 110. Šie pirmieji bitai nurodė diapazonus, kuriuose buvo išduotas konkrečios klasės adresas: 0.0.0.0 - 127.255.255.255 A klasei, 128.0.0.0 - 191.255.255.255 - B klasė, 192.0.0.0 - 223.255.255.255 - C klasė. Internetui išplitus po visą planetą, reguliuotojai suprato, kad nepateko į tikslą, ir 90-ųjų pradžioje jie sukūrė beklasės koncepciją, kuri leido neprisirišti prie pirmaujančių bitų. Šiek tiek daugiau informacijos galite rasti, tarkime, .
Pereikime prie praktikos
Praktiškai įgyvendinsime tris labiausiai tikėtinas, kaip man atrodė, užduotis:
- gauti pirmąjį ir paskutinį diapazono adresą;
- gauti kitą nurodytą dydžio diapazoną (CIDR);
- patikrinti, ar adresas priklauso diapazonui.
Diegimas bus skirtas IPv6, tačiau prireikus logika gali būti lengvai pritaikyta. Turiu keletą idėjų , bet įgyvendino šiek tiek kitaip. Be to, pavyzdžiuose netikrinama, ar nėra įvesties klaidų. Taigi, eime.
Kaip jau minėjau, pirmasis ir paskutinis diapazono adresai gali būti nustatyti naudojant bitines operacijas, žinant diapazono pradžią ir dvejetainę potinklio kaukę. Atitinkamai, pirmas dalykas, kurį turime padaryti, yra paversti CIDR dvejetaine kauke. Norėdami tai padaryti, surinksime jo šešioliktainį vaizdą ir supakuosime į dvejetainį.
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);
}Вызов pakuotė ('H*', $kaukė) supakuoja šešioliktainį vaizdą taip pat kaip inet_pton(). Skirtumas tik tas, kad skambinant paketas() visi 0 turi būti savo vietose, o įraše neturėtų būti dvitaškių, kitaip nei žmogaus skaitomas įrašas.
Kitas žingsnis – apskaičiuoti diapazono pradžią ir pabaigą. Ir čia atsiranda niuansai. Bitines operacijas riboja procesoriaus bitų talpa. Atitinkamai, mano 32 bitų „CubieTruck“, kurį kartais naudoju visokiam bandomajam pamaloninimui, nebus įmanoma apdoroti visų 128 adreso bitų viena operacija. Tačiau niekas netrukdo mums jo suskirstyti į 32 bitų grupes (tik tuo atveju, kas žino, kokiais procesoriais dirbsime).
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);
}
}Būsimam naudojimui suteiksime galimybę perduoti IP ir gauti rezultatą tiek dvejetaine, tiek žmogaus skaitoma forma. Parametras $kuris čia nurodoma, ar norime gauti diapazono pradžią ar pabaigą (reikšmes 'pradėti' arba 'galas' atitinkamai).
Kita užduotis (ir praktiškiausia mūsų įmonei) yra kito diapazono apskaičiavimas. Šiai užduočiai nieko geresnio neatėjo į galvą, kaip išplėsti adresą į dvejetainę eilutę ir pridėti 1 į norimą vietą, o tada viską sutraukti atgal. Kad bet kur neatsirastų artefaktų, skaidymo ir surinkimo metu nusprendžiau adresą padalinti baitais.
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);
}
}Išvestyje gauname kito dydžio diapazono priešdėlį, nurodytą $cidr. Naudodami šią funkciją savo klientams skiriame adresų blokus.
Galiausiai patikrinama, ar adresas priklauso diapazonui. Pavyzdžiui, vieną bloką /48 skyrėme blokams paskirstyti klientams /64, ir turime pasirūpinti, kad priskirdami neperžengtume skirto bloko (praktikoje tai įvyks dar negreitai, bet vis tiek yra galimybė). Čia viskas paprasta. Diapazono pradžią ir pabaigą gauname dvejetaine forma ir patikriname, ar adresas yra ribose.
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);
}Tikiuosi, kad tai buvo naudinga. Kokios kitos funkcijos dirbant su adresais galėtų būti naudingos, jūsų nuomone? Visi papildymai, komentarai ir kodų apžvalgos yra labai laukiami komentaruose.
Jei jau esate mūsų klientas arba dar tik galvojate juo tapti, šio straipsnio publikavimo proga siūlome jums gauti bloką /64 visiškai nemokama visoms VPN paslaugoms arba skirtam serveriui Equinix Tier IV duomenų centre, Nyderlanduose, paprašius pardavimo skyriui, biliete pateikiant nuorodą į šį straipsnį. Pasiūlymas galioja iki 2020 kovo mėn.
Kai kurie skelbimai 🙂
Dėkojame, kad likote su mumis. Ar jums patinka mūsų straipsniai? Norite pamatyti įdomesnio turinio? Palaikykite mus pateikdami užsakymą ar rekomenduodami draugams, , unikalus pradinio lygio serverių analogas, kurį mes sugalvojome jums: (galima su RAID1 ir RAID10, iki 24 branduolių ir iki 40 GB DDR4).
„Dell R730xd“ 2 kartus pigiau „Equinix Tier IV“ duomenų centre Amsterdame? Tik čia Olandijoje! „Dell R420“ – 2 x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gbps 100 TB – nuo 99 USD! Skaityti apie
Šaltinis: www.habr.com
