Nemrég megkaptuk az LIR státuszt és egy /29 IPv6 blokkot. Ezt követően szükségessé vált a kijelölt alhálózatok nyilvántartásának vezetése. Mivel a billing rendszerünk PHP-ben íródott, kicsit jobban el kellett mélyednünk a kérdésben, és rájöttünk, hogy ez a nyelv nem a legbarátságosabb az IPv6 kezelésében. Az alábbiakban bemutatjuk a címekkel és tartományokkal kapcsolatos feladatok megoldását. Lehet, hogy nem a legkifinomultabb, de a kitűzött célokat teljesíti.

Egy kis elmélet
Nyilatkozat. Ha ismeri az IPv6-ot és a vele kapcsolatos ismereteket, ez a rész talán unalmas lehet az Ön számára. Talán nem is.
Az embereket, akik először találkoznak az IPv6 notációval, meglepetés érheti. A kifinomult 64.233.177.101 mellett hirtelen találkozunk a 2607:f8b0:4002:c08::8b címkével, és elbizonytalanodhatunk. Az előbbi és az utóbbi is csupán ember által könnyen olvasható 32 és 128 bit értéket képvisel. Minden IP csomag egy olyan fejlécet tartalmaz, amelyben a bitek pontosan standardizált célt szolgálnak. Anélkül, hogy mélyebben belemernénk a fejléc struktúrájába, fontos megértenünk, hogy az IP címekkel és tartományokkal végzett műveletekhez leginkább a bináris matematikát és bitműveleteket célszerű alkalmazni. A legjobban a következő formában célszerű tárolni őket: BINARY(4) IPv4 esetén és BINARY(16) IPv6 esetén.
Egy másik fontos aspektus, amelyet érdemes lenne érinteni, a hálózati maszkok és a CIDR notáció. A CIDR az osztály nélküli köztes irányítást jelenti (). Ez a koncepció váltotta fel az osztályozott megközelítést a kérdésben, hogy az IP cím mely része a hálózati prefix, és melyik a hálózati interfész címe ezen a hálózaton belül. A gyakorlatban az első n bit, amelyek a prefixet képviselik, 1 értéket kapnak, a maradék 0-t.
Ember által olvasható formában ez így van megadva: ip.add.re.ss/cidr. Például, 64.233.177.0/24 jelzi, hogy az első 24 bit a prefixhez tartozik. Az utolsó 8 bit, vagyis az ember által olvasható cím végső száma, a szubneten belüli címhez tartozik. Néhány további példát nézzünk. 64.233.177.101/32 és 2607:f8b0:4002:c08::8b/128 — egy konkrét cím. 2607:f8b0:4002:c08::/64 — az első 64 bit (az első 4 csoport) a prefix, a következő 64 bit pedig a lokális rész. Érdekesség, hogy ha valakit zavar a "::" a notációban, a dupla kettőspont tetszőleges számú zérót tartalmazó szekciót helyettesít. Ez a notációban csak egyszer fordulhat elő. Más szavakkal, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.
Mit mit kell ebből az egészből kiemelnünk? Először is, az alhálózat első és utolsó címe a bináris AND és OR segítségével érhető el, ha ismerjük a maszkot bináris formában. Másodszor, a következő alhálózat méretét (azaz a CIDR) n úgy számíthatjuk ki, hogy 1-et adunk hozzá a n-dik pozícióhoz a bináris reprezentációban. A bináris formán azokat az eredményeket értem, amelyeket a következő funkciók segítségével kapunk: pack() és inet_pton() és a következő használatával: , a bináris alatt pedig azt a bináris rendszert értem, amelyet például a következő funkcióval kaphatunk: base_convert().
Történelmi háttérA klasszifikált címzés előtt már az osztály alapú szegmentáció is létezett. Azokban a régi időkben senki sem gondolta, hogy ilyen sok alhálózat lesz, nagy blokkokban osztogatni kezdték őket jobbra-balra: A osztály - a prefix az első 8 bit volt (azaz az első szám), vezető bitje 0; B osztály - az első 16 (az első két szám), vezető bitek 10; C osztály - az első 24 bit, vezető bitek 110. Ezek a vezető bitek határozták meg az osztályba tartozó címek kibocsátási tartományait: 0.0.0.0 — 127.255.255.255 A osztályhoz, 128.0.0.0 — 191.255.255.255 — B osztály, 192.0.0.0 — 223.255.255.255 — C osztály. Ahogy az internet terjedt a világban, a szabályozók rájöttek, hogy hibáztak, és az 1990-es évek elején kidolgozták a klasszifikálatlan koncepciót, amely lehetővé tette, hogy ne ragaszkodjanak a vezető bitekhez. Egy kicsit több részletet találhatunk például a .
Térjünk át a gyakorlatra
A gyakorlatban három legvalószínűbb feladatot valósítunk meg, úgy gondolom:
- az első és az utolsó cím megkapása a tartományban;
- a megadott méretű következő tartomány megszerzése (CIDR);
- a cím tartományhoz való tartozásának ellenőrzése.
A megvalósítás IPv6-ra vonatkozik, de ha szükséges, a logika könnyen alkalmazható. Néhány ötletet érdekében jelentett, de kissé másképp valósítottam meg. Az példákban nincs bemeneti hibaellenőrzés. Tehát lássunk neki. Ahogyan említettem, az első és utolsó cím a tartományban bitműveletek segítségével határozható meg, ha tudjuk a tartomány elejét és a bináris alhálózati maszkot. Ennek megfelelően első lépésként a CIDR-t bináris maszkká kell alakítanunk. Ehhez összegyűjtjük hexadecimális reprezentációját és binárisra csomagoljuk.
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); }
pack(‘H*’, $mask)Hívás a hexadecimális reprezentációt ugyanúgy csomagolja, mint a . Az egyetlen különbség az, hogy a híváskor inet_pton(). Csak annyival különbözik, hogy a hívás során pack() Minden 0-nak a helyén kell lennie, és a feljegyzésben kettőspontnak nem szabad lennie, ellentétben az ember által érthető feljegyzéssel.
A következő lépés a tartomány elejének és végének kiszámítása. Itt jelentkeznek a bonyodalmak. A bitműveletek a processzor bitméretére korlátozódnak. Tehát a 32 bites CubieTruck-on, amelyet néha különféle tesztelési célokra használok, nem lehet a teljes 128 bit címét egy műveletben feldolgozni. Azonban semmi sem akadályoz meg abban, hogy 32 bites csoportokra bontsuk (elővigyázatosságból, ki tudja, milyen processzorokon fogunk futni).
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);
}
}Jövőbeni felhasználásra tervezzük a lehetőséget, hogy IP-t adjunk át, és az eredményt bináris és ember által olvasható formában kapjuk meg. A paraméter $which itt azt határozza meg, hogy a tartomány elejét vagy végét szeretnénk-e kapni (előtagok 'start' vagy 'end' vagyis).
A következő feladat (ráadásul a legpraktikusabb a cégünk számára) a következő tartomány kiszámítása. Erre a feladatra nem jutott jobb eszembe, mint a címet bináris formába bontani, és 1-et hozzáadni a szükséges pozícióban, majd mindent visszaállítani. Annak érdekében, hogy ne keletkezzenek artefaktumok, a cím bontásakor és összerakásakor bájtonként akartam dolgozni.
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);
}
}A kimenetben megkapjuk a következő tartomány prefixét, amelynek méretét a megadott $cidr. Ezt a funkciót használjuk a címblokkok ügyfeleinknek való kiemelésére.
Végül ellenőrizzük, hogy a cím a tartományban van-e. Például egy /48-as blokkot foglaltunk le az ügyfeleknek /64-es blokkok kiosztására, és meg kell győződnünk arról, hogy a kiosztás során nem lépjük túl a fenntartott blokk határait (a gyakorlatban ez nem fog hamar bekövetkezni, de a lehetőség mindig fennáll). Itt minden egyszerű. Megkapjuk a tartomány elejét és végét bináris formában, és ellenőrizzük, hogy a cím a határokon belül van-e.
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);
}Remélem, hasznos volt. Milyen más címkezelő funkciókat tartana hasznosnak? Bármilyen kiegészítést, megjegyzést és kód-értékelést szívesen látok a hozzászólásokban.
Ha Ön már ügyfelünk, vagy csak fontolgatja, hogy azzá váljon, az e cikk megjelenése alkalmából felajánljuk, hogy ingyen kaphat egy /64 blokkot minden VPS vagy dedikált szerver szolgáltatásunkhoz az Equinix Tier IV adatközpontban, Hollandiában, árajánlatkérő jegy benyújtásával, amelyben megadja ezt a cikket. Az ajánlat 2020 márciusáig érvényes.
Egy kis reklám 🙂
Köszönjük, hogy velünk maradtál. Tetszenek a cikkeink? Szeretnél több érdekes anyagot látni? Támogass minket, és rendelés leadásával vagy ismerősöknek ajánlással segíts nekünk. , Egyedülálló alternatívája az entry-level szervereknek, amelyet Önnek találtunk ki: (elérhetők RAID1 és RAID10 opciók, akár 24 magig és 40GB DDR4-ig).
Dell R730xd kétszer olcsóbban a Tier IV Equinix adatközpontban Amszterdamban? Csak nálunk Hollandiában! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — 99 dollártól! Olvasd el ezt
Forrás: habr.com
