Kami baru-baru ini menerima status LIR dan blok /29 IPv6. Dan kemudian muncul kebutuhan untuk melacak subnet yang ditugaskan. Dan karena penagihan kami ditulis dalam PHP, kami harus menyelidiki masalah ini sedikit dan menyadari bahwa bahasa ini bukan yang paling ramah dalam bekerja dengan IPv6. Di bawah ini adalah solusi kami untuk masalah yang muncul saat bekerja dengan alamat dan rentang. Mungkin bukan yang paling elegan, tapi berhasil.

Sedikit teori
Penafian. Jika Anda sudah familiar dengan apa itu IPv6 dan apa yang terkandung di dalamnya, bagian ini mungkin membosankan bagi Anda. Mungkin tidak.
Orang yang melihat anotasi IPv6 untuk pertama kalinya mungkin merasa ini cukup menakutkan. Setelah elegan 64.233.177.101 kita tiba-tiba dihadapkan pada 2607:f8b0:4002:c08::8b dan kita mungkin menjadi bingung. Keduanya hanyalah representasi masing-masing 32 dan 128 bit yang dapat dibaca manusia. Setiap paket IP berisi header dengan tujuan yang distandarisasi secara ketat untuk setiap bit. Tanpa mempelajari lebih dalam struktur header, satu hal yang perlu kita ambil dari sini adalah bahwa untuk operasi dengan alamat dan rentang IP, biasanya lebih mudah menggunakan matematika biner dan operasi bitwise. Juga paling mudah untuk menyimpannya dalam database sebagai BINER(4) untuk IPv4 dan BINER(16) untuk IPv6.
Aspek penting lainnya yang patut disinggung adalah network mask dan notasi CIDR. CIDR adalah singkatan dari Classless Inter-Domain Routing (). Konsep ini telah menggantikan konsep kelas dalam menentukan bagian mana dari alamat IP yang merupakan awalan jaringan, dan bagian mana yang merupakan alamat antarmuka jaringan dalam jaringan ini. Dalam praktiknya, n bit pertama yang sesuai dengan awalan akan disetel ke 1, sisanya ke 0.
Dalam bentuk yang dapat dimengerti manusia, ini ditulis dalam bentuk ip.add.re.ss/cidr. Misalnya, 64.233.177.0/24 menunjukkan bahwa 24 bit pertama mengacu pada awalan. 8 bit terakhir, juga dikenal sebagai angka terakhir dalam notasi yang dapat dibaca manusia, mengacu pada alamat dalam subnet. Beberapa latihan lagi. 64.233.177.101/32 и 2607:f8b0:4002:c08::8b/128 - satu alamat tertentu. 2607:f8b0:4002:c08::/64 - 64 bit pertama (4 grup pertama) adalah awalan, 64 bit sisanya adalah bagian lokal. Ngomong-ngomong, jika ada yang bingung dengan “::” di entri, titik dua ganda menggantikan sejumlah bagian yang mengandung 0. Ini hanya dapat muncul sekali dalam anotasi. Dengan kata lain, 2607:f8b0:4002:c08::8b = 2607:f8b0:4002:c08:0:0:0:8b.
Apa yang perlu kita pelajari dari semua ini? Pertama, alamat subnet pertama dan terakhir dapat diperoleh dengan menggunakan biner AND dan OR, dengan mengetahui mask dalam bentuk biner. Kedua, ukuran subnet berikutnya (yaitu dengan CIDR) n dapat dihitung dengan menambahkan 1 ke n-posisi itu dalam representasi biner. Yang saya maksud dengan biner adalah hasil penggunaan fungsi mengemas() и inet_pton() dan penggunaan lebih lanjut , dengan biner adalah representasi sistem biner yang dapat diperoleh dengan, katakanlah, konversi_dasar().
sejarahPengalamatan tanpa kelas didahului dengan pemisahan kelas. Pada tahun-tahun yang jauh itu, tidak ada yang membayangkan bahwa akan ada begitu banyak subnet; mereka didistribusikan ke kiri dan ke kanan dalam blok besar: kelas A - awalannya adalah 8 bit pertama (yaitu angka pertama), dengan bit awal 0; kelas B - 16 pertama (dua angka pertama), bit terdepan 10; kelas C - 24 bit pertama, bit terdepan 110. Bit terdepan ini menentukan rentang di mana alamat kelas tertentu dikeluarkan: 0.0.0.0 - 127.255.255.255 untuk kelas A, 128.0.0.0 - 191.255.255.255 - kelas B, 192.0.0.0 - 223.255.255.255 - kelas C. Ketika Internet menyebar ke seluruh dunia, regulator menyadari bahwa mereka telah meleset, dan di awal tahun 90an mereka mengembangkan konsep tanpa kelas yang memungkinkan untuk tidak terikat ke bit terdepan. Sedikit lebih detail dapat ditemukan di, katakanlah, .
Mari kita lanjutkan ke latihan
Dalam praktiknya, kami akan menerapkan tiga tugas yang paling mungkin, menurut saya:
- mendapatkan alamat pertama dan terakhir dari jangkauan;
- dapatkan rentang ukuran tertentu berikutnya (CIDR);
- memeriksa apakah suatu alamat termasuk dalam suatu rentang.
Implementasinya akan ditujukan untuk IPv6, namun jika perlu, logikanya dapat dengan mudah diadaptasi. Saya punya beberapa ide , tetapi menerapkannya sedikit berbeda. Selain itu, contoh tidak memeriksa kesalahan input. Jadi ayo pergi.
Seperti yang telah saya sebutkan, alamat pertama dan terakhir suatu rentang dapat ditentukan menggunakan operasi bitwise, mengetahui awal rentang dan subnet mask biner. Oleh karena itu, hal pertama yang perlu kita lakukan adalah mengubah CIDR menjadi topeng biner. Untuk melakukan ini, kami akan mengumpulkan representasi hexnya dan mengemasnya ke dalam biner.
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);
}Tantangan paket('H*', $mask) mengemas representasi hex dengan cara yang sama seperti inet_pton(). Satu-satunya perbedaan adalah saat menelepon mengemas() semua angka 0 harus berada di tempatnya masing-masing, dan tidak boleh ada titik dua pada entri, tidak seperti entri yang dapat dibaca manusia.
Langkah selanjutnya adalah menghitung awal dan akhir rentang. Dan di sinilah nuansa muncul. Operasi bitwise dibatasi oleh kapasitas bit prosesor. Oleh karena itu, pada CubieTruck 32-bit saya, yang terkadang saya gunakan untuk semua jenis pengujian yang memanjakan, tidak mungkin memproses semua 128 bit alamat dalam satu operasi. Namun, tidak ada yang menghalangi kami untuk membaginya menjadi kelompok 32 bit (untuk berjaga-jaga, siapa yang tahu prosesor apa yang akan kami gunakan).
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);
}
}Untuk penggunaan di masa mendatang, kami akan menyediakan kemampuan untuk mengirimkan IP dan menerima hasilnya dalam bentuk biner dan dapat dibaca manusia. Parameter $yang mana di sini ini menentukan apakah kita ingin mendapatkan awal atau akhir rentang (nilai 'Mulailah' или 'akhir' masing-masing).
Tugas selanjutnya (dan juga yang paling praktis bagi perusahaan kami) adalah menghitung kisaran berikutnya. Untuk tugas ini, tidak ada hal yang lebih baik yang terlintas dalam pikiran selain memperluas alamat menjadi string biner dan menambahkan 1 pada posisi yang diinginkan, lalu menciutkan semuanya kembali. Untuk menghindari artefak muncul di mana pun, saya memutuskan untuk membagi alamat berdasarkan byte selama dekomposisi dan perakitan.
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);
}
}Pada output kita mendapatkan awalan dari rentang ukuran berikutnya yang ditentukan $cidr. Dengan fungsi ini kami mengalokasikan blok alamat ke klien kami.
Terakhir, memeriksa apakah alamat tersebut termasuk dalam rentang tersebut. Misalnya, kami mengalokasikan satu blok /48 untuk mendistribusikan blok ke klien /64, dan kita perlu memastikan bahwa saat menugaskan kita tidak melampaui blok yang dialokasikan (dalam praktiknya hal ini tidak akan terjadi dalam waktu dekat, tetapi masih ada kemungkinan). Semuanya sederhana di sini. Kami mendapatkan awal dan akhir rentang dalam bentuk biner dan memeriksa apakah alamatnya berada dalam batas.
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);
}Saya harap ini bermanfaat. Apa fungsi lain untuk bekerja dengan alamat yang mungkin berguna menurut Anda? Setiap penambahan, komentar, dan tinjauan kode disambut dengan hangat di komentar.
Jika Anda sudah menjadi klien kami atau baru berpikir untuk menjadi klien kami, pada kesempatan publikasi artikel ini kami menawarkan Anda untuk menerima blok /64 sepenuhnya gratis untuk semua layanan vps atau server khusus di pusat data Equinix Tier IV, Belanda atas permintaan ke bagian penjualan dengan memberikan tautan ke artikel ini di tiket. Penawaran ini berlaku hingga Maret 2020.
Beberapa iklan 🙂
Terima kasih untuk tetap bersama kami. Apakah Anda menyukai artikel kami? Ingin melihat konten yang lebih menarik? Dukung kami dengan melakukan pemesanan atau merekomendasikan kepada teman, , analog unik dari server level awal, yang kami temukan untuk Anda: (tersedia dengan RAID1 dan RAID10, hingga 24 core dan hingga 40GB DDR4).
Dell R730xd 2x lebih murah di pusat data Equinix Tier IV di Amsterdam? Hanya disini di Belanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - mulai $99! Membaca tentang
Sumber: www.habr.com
