Nedavno smo dobili status LIR i /29 blok IPv6. I ubrzo se pojavila potreba za vođenjem evidencije o dodeljenim podmrežama. Pošto je naš billing pisan u PHP-u, morali smo malo da se udubimo u ovu temu i shvatimo da ovaj jezik nije najprijateljskiji kada je u pitanju rad sa IPv6. Ispod su naši odgovori na izazove vezane za rad sa adresama i opsezima. Možda ne najsitniji, ali zadaci su ispunjeni.

Nekaj teorije
Zavrnitev odgovornosti. Ako ste upoznati sa tim šta je IPv6 i kako se koristi, ovaj deo može biti dosadan za vas. Možda i neće.
Ljude koji prvi put vide anotaciju IPv6, ona može prilično zaplašiti. Nakon elegantnih 64.233.177.101 srećemo se sa 2607:f8b0:4002:c08::8b i možemo biti zbunjeni. I jedno i drugo su samo ljudski razumljivi prikazi 32 i 128 bita. Svaki IP paket sadrži zaglavlje sa strogo standardizovanom svrhom svake bita. Ne ulazeći dublje u strukturu zaglavlja, odavde možemo izvući jedno: za operacije sa IP adresama i opsezima najpraktičnije je koristiti binarnu matematiku i bit-operacije. Takođe je najlakše čuvati ih u bazi kao BINARY(4) za IPv4 i BINARY(16) za IPv6.
Još jedan važan aspekt koji bi bilo dobro spomenuti su maske mreže i CIDR notacija. CIDR je akronim od Classless Inter-Domain Routing (). Ova koncepcija je došla kao zamena klasičnoj u pitanju određivanja koji deo IP adrese je prefiks mreže, a koji je adresa mrežnog interfejsa unutar te mreže. U praksi, prvih n bita koji odgovaraju prefiksu biće postavljeni na 1, a ostalim na 0.
U ljudski razumljivom obliku ovo se zapisuje kao ip.add.re.ss/cidr. Na primer, 64.233.177.0/24 označava da prvih 24 bita pripadaju prefiksu. Poslednjih 8 bitova, koji su takođe poslednji broj u ljudski razumljivom zapisu, se odnosi na adresu unutar podmreže. Još nekoliko vežbi. 64.233.177.101/32 in 2607:f8b0:4002:c08::8b/128 — jedna konkretna adresa. 2607:f8b0:4002:c08::/64 — prvih 64 bita (prvih 4 grupe) su prefiks, ostalih 64 bita su lokalni deo. Usput, ako nekoga zbunjuje "::" u zapisu, dvostruka dvotačka zamenjuje proizvoljan broj sekcija koje sadrže 0. Može se pojaviti u anotaciji samo 1 put. Drugim rečima, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.
Kaj bi se iz tega naučili? Prvi in zadnji naslov podomrežja lahko dobimo z binarnim AND in OR, če poznamo masko v binarni obliki. Drugič, naslednjo podomrežje velikosti (t.j. s CIDR) n lahko izračunamo, če dodamo 1 na n-to pozicijo v binarni predstavitvi. Pod binarno obliko imam v mislih rezultat uporabe funkcij pack() in inet_pton() in nadaljnjo uporabo pod binarnim - predstavitev v binarni sistem, ki ga lahko dobimo, recimo, s pomočjo base_convert().
Zgodovinski pregledPred razrednim je bila razredna segregacija naslovov. V tistih davnih časih nihče ni predvideval, da bo toliko podomrežij, delili so jih na vsakem koraku v velikih blokih: razred A - prvi 8 bitov (t.j. prvo število), s vodilnim bitom 0; razred B - prvih 16 (prva dve številki), vodilni biti 10; razred C - prvih 24 bitov, vodilni biti 110. Ti vodilni biti so določali območja, v katerih so podeljevali naslov določenega razreda: 0.0.0.0 — 127.255.255.255 za razred A, 128.0.0.0 — 191.255.255.255 - razred B, 192.0.0.0 - 223.255.255.255 - razred C. Ko se je internet širila po planetu, so regulatorji spoznali, da so zgrešili, in v začetku 90-ih razvili koncept brez razredov, ki je omogočil, da se ne povezujejo z vodilnimi biti. Nekaj več podrobnosti lahko izveste, recimo, v .
Preidimo k praksi
V praksi bomo izvedli tri najbolj verjetne naloge, kot sem jih razumel:
- pridobitev prvega in zadnjega naslova območja;
- pridobitev naslednjega območja določene velikosti (CIDR);
- preverjanje pripadnosti naslova območju.
Izvedba bo za IPv6, vendar je logiko mogoče enostavno prilagoditi po potrebi. Nekatere ideje sem pridobil, , vendar sem izvedbo nekoliko spremenil. Tudi v primerih ni preverjanja napak pri vnosu. Tako, gremo naprej.
Kot sem že omenil, lahko prvi in zadnji naslov območja določimo z bitnimi operacijami, če poznamo začetek območja in binarno masko podomrežja. Zato moramo najprej pretvoriti CIDR v binarno masko. Za to bomo pripravili hex predstavitev in jo zapakirali v binarno.
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);
}Poziv pack(‘H*’, $mask) pakira hex predstavitev enako, kot tudi inet_pton(). Razlika je le v tem, da pri klicu pack() vse 0 morajo biti na svojih mestih, dvojnih točk v zapisu pa ne sme biti, v nasprotju s človeku razumljivim zapisom.
Naslednji korak je izračun začetka in konca obsega. In tu se bodo pojavile podrobnosti. Bitne operacije so omejene z razpredelnico procesorja. Zato na mojem 32-bitnem CubieTrucku, ki ga včasih uporabljam za razne testne zadeve, vseh 128 bitov naslova ne morem obdelati v eni operaciji. Vendar nam nič ne preprečuje, da ga razdelimo v skupine po 32 bitov (za vsak slučaj, kdo ve, na katerih procesorjih bomo delovali).
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);
}
}Za prihodnjo uporabo bomo predvideli možnost posredovanja IP in pridobivanja rezultata tako v binarni kot v človeku berljivi obliki. Parameter $which tukaj določa, ali želimo pridobiti začetek ali konec obsega (vrednosti 'start' ili 'end' ustrezno).
Naslednja naloga (poleg tega najbolj praktična za naše podjetje) je izračun naslednjega obsega. Za to nalogo mi ne pride na misel nič boljšega, kot da naslov razdelim v binarno vrstico in dodam 1 na pravem mestu, nato pa vse znova sestavim. Da ne bi prišlo do artefaktov, sem se odločil, da pri razdelitvi in sestavljanju razbijem naslov na bajte.
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 izhodu bomo dobili prefiks naslednjega obsega velikosti, navedene v $cidr.S to funkcijo dodeljujemo bloke naslovov našim strankam.
Končno preverjanje, ali naslov pripada obsegu. Na primer, dodelili smo en blok /48 za dodeljevanje blokov /64 strankam in moramo preveriti, ali pri dodelitvi ne presegamo dodeljenega bloka (v praksi se to ne bo zgodilo kmalu, a vseeno obstaja verjetnost). Tudi tukaj je preprosto. Pridobimo začetek in konec obsega v binarni obliki in preverimo, ali je naslov znotraj meja.
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);
}Upam, da je bilo to koristno. Katerih drugih funkcij za delo z naslovi se vam zdi, da bi bile koristne? Vsaka dopolnitev, komentar in kode pregled so dobrodošli v komentarjih.
Če ste že naš strank ali se šele odločate, ali boste postali, vam ob izdaji tega članka ponujamo, da pridobite blok /64 popolnoma brezplačno za vse storitve VPS ali namenski strežnik v podatkovnem centru Equinix Tier IV na Nizozemskem na zahtevo v prodajnem oddelku, s tako povezavo do tega članka v tiketu. Ponudba velja do marca 2020.
Malo reklame 🙂
Hvala što ostajete s nama. Da li vam se sviđaju naši članci? Želite li videti više zanimljivih materijala? Podržite nas tako što ćete naručiti ili preporučiti prijateljima, , jedinstveni ekvivalent entry-level servera koji smo stvorili za vas: (options available with RAID1 and RAID10, up to 24 cores and up to 40GB DDR4).
Dell R730xd je dvostruko jeftiniji u Equinix Tier IV data centru u Amsterdamu? Only with us in the Netherlands! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — from $99! Read about how
Izvor: habr.com
