Bekerja dengan IPv6 di PHP

Kami baru-baru ini menerima status LIR dan / 29 blok IPv6. Dan kemudian ada kebutuhan untuk melacak subnet yang ditunjuk. Dan karena tagihan kami ditulis dalam PHP, saya harus sedikit terinspirasi oleh masalah ini dan menyadari bahwa bahasa ini bukan yang paling ramah dalam hal bekerja dengan IPv6. Di bawah cut - solusi kami untuk masalah bekerja dengan alamat dan rentang. Mungkin bukan yang paling elegan, tetapi melakukan tugas.



Sedikit teori


Penafian. Jika Anda terbiasa dengan apa itu IPv6 dan apa yang dimakannya, bagian ini mungkin membosankan bagi Anda. Mungkin tidak.

Orang yang pertama kali melihat penjelasan IPv6 mungkin akan kecewa. Setelah 64.233.177.101 yang elegan , kami tiba-tiba menemukan 2607: f8b0: 4002: c08 :: 8b dan dapat menjadi bingung. Baik itu, dan yang lain - hanya representasi yang dapat dibaca manusia masing-masing 32 dan 128 bit. Paket IP apa pun berisi tajuk dengan tugas terstandarisasi ketat dari setiap bit. Tanpa masuk lebih jauh ke dalam struktur header, kita perlu mengeluarkan satu hal dari sini: untuk operasi dengan alamat dan rentang IP umumnya lebih mudah menggunakan matematika biner dan operasi bitwise. Juga paling mudah untuk menyimpannya dalam database sebagai BINARY (4) untuk IPv4 dan BINARY (16) untuk IPv6.

Aspek penting lain yang harus diatasi adalah topeng jaringan dan notasi CIDR. CIDR adalah akronim untuk Routing Antar-Domain Tanpa Kelas. Konsep ini menggantikan kelas satu dalam pertanyaan menentukan bagian mana dari alamat IP yang merupakan awalan jaringan dan bagian mana yang merupakan alamat antarmuka jaringan dalam jaringan ini. Dalam prakteknya, n bit pertama yang sesuai dengan awalan akan diatur ke 1, sisanya ke 0.

Dalam bentuk yang dapat dibaca manusia, ini ditulis sebagai ip.add.re.ss. / cidr . Misalnya, 64.233.177.0/24 berarti 24 bit pertama merujuk pada awalan. 8 bit terakhir, mereka adalah angka terakhir dalam entri yang dapat dibaca manusia, merujuk ke alamat di dalam subnet. Beberapa lagi latihan. 64.233.177.101/32 dan 2607: f8b0: 4002: c08 :: 8b / 128 - satu alamat spesifik. 2607: f8b0: 4002: c08 :: / 64 - 64 bit pertama (4 grup pertama) - awalan, 64 bit yang tersisa - bagian lokal. Ngomong-ngomong, jika ada yang merasa malu dengan "::" dalam entri, usus besar ganda menggantikan sejumlah bagian yang berisi 0. Secara acak dapat terjadi dalam anotasi hanya 1 kali. 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, mengetahui mask dalam bentuk biner. Kedua, subnet ukuran berikutnya (yaitu, dengan CIDR) n dapat dihitung dengan menambahkan 1 ke posisi ke- n dalam representasi biner. Dengan tampilan biner, maksud saya adalah hasil dari menggunakan paket fungsi () dan inet_pton () dan penggunaan lebih lanjut dari operator bitwise , oleh biner - representasi dalam sistem biner, yang dapat diperoleh, katakanlah, menggunakan base_convert () .

Latar belakang sejarah
Segregasi tanpa kelas untuk menangani didahului tanpa kelas. Pada tahun-tahun yang jauh itu, tidak ada yang mengira bahwa akan ada begitu banyak subnet, mereka didistribusikan ke kanan dan kiri dalam blok besar: kelas A - 8 bit pertama (mis., Angka pertama) diawali, dengan bit 0 terkemuka; kelas B - 16 pertama (dua angka pertama), bit terkemuka 10; kelas C - 24 bit pertama, bit terkemuka 110. Bit terkemuka yang sama ini menetapkan rentang di mana alamat kelas 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 planet ini, regulator menyadari bahwa mereka telah ketinggalan, dan pada awal 90-an mengembangkan konsep tanpa kelas, yang memungkinkan mereka untuk tidak terikat pada bit terkemuka. Sedikit lebih banyak detail dapat ditemukan, katakanlah, dalam yang agung dan maha tahu .


Mari kita lanjutkan berlatih


Dalam praktiknya, kami mengimplementasikan tiga tugas yang paling mungkin, menurut saya, adalah:

  1. mendapatkan alamat pertama dan terakhir rentang;
  2. mendapatkan rentang ukuran tertentu (CIDR) berikutnya;
  3. memeriksa apakah alamat tersebut termasuk dalam kisaran.

Implementasinya akan untuk IPv6, tetapi jika perlu, logikanya dapat dengan mudah diadaptasi. Saya mendapat beberapa ide dari sini , tetapi menerapkannya sedikit berbeda. Juga dalam contoh tidak ada pemeriksaan untuk kesalahan input. Jadi ayo pergi.

Seperti yang saya sebutkan, alamat pertama dan terakhir dari suatu range dapat ditentukan menggunakan operasi bitwise, mengetahui awal dari range dan subnet mask biner. Dengan demikian, hal pertama yang perlu kita lakukan adalah mengubah CIDR menjadi topeng biner. Untuk melakukan ini, kumpulkan representasi hex-nya dan kemas 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); } 

Paket panggilan ('H *', $ mask) mengemas representasi hex dengan cara yang sama seperti inet_pton () . Satu-satunya perbedaan adalah ketika paket () dipanggil, semua 0 harus ada di tempatnya, dan seharusnya tidak ada titik dua dalam entri, tidak seperti entri yang dapat dibaca manusia.

Langkah selanjutnya adalah menghitung awal dan akhir rentang. Dan di sini ada nuansa. Pengoperasian bitwise dibatasi oleh kapasitas prosesor. Oleh karena itu, pada CubieTruck 32-bit saya, yang kadang-kadang saya gunakan untuk pengujian yang memanjakan, semua 128 bit alamat tidak dapat diproses dalam satu operasi. Namun, tidak ada yang mencegah kita dari memecahnya menjadi kelompok 32 bit (untuk berjaga-jaga, siapa yang tahu prosesor apa yang akan kita jalankan).

 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 memberikan kemampuan untuk mengirimkan IP dan mendapatkan hasilnya dalam bentuk biner dan dalam bentuk yang dapat dibaca manusia. Parameter $ which di sini menetapkan apakah kita ingin mendapatkan awal atau akhir kisaran (nilainya masing-masing adalah 'mulai' atau 'akhir' ).

Tugas selanjutnya (selain yang paling praktis untuk perusahaan kami) adalah menghitung rentang berikutnya. Untuk tugas ini, tidak ada yang lebih baik terlintas dalam pikiran, kecuali bagaimana menguraikan alamat menjadi string biner dan menambahkan 1 pada posisi yang diinginkan, kemudian runtuhkan semuanya kembali. Untuk mencegah artefak muncul di mana saja, saya memutuskan untuk membagi alamat dengan 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 kisaran ukuran berikutnya yang ditentukan dalam $ cidr . Dengan fungsi ini, kami mengalokasikan blok alamat ke pelanggan kami.

Terakhir, periksa apakah alamat tersebut termasuk dalam kisaran tersebut. Sebagai contoh, kami mengalokasikan satu / 48 blok untuk distribusi / 64 blok kepada pelanggan, dan kami perlu memastikan bahwa selama penunjukan kami tidak melampaui blok yang dialokasikan (dalam praktiknya ini akan segera terjadi, tetapi masih ada peluang). Semuanya sederhana di sini. Kami mendapatkan awal dan akhir rentang dalam bentuk biner dan memeriksa apakah alamatnya ada di dalam.

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

Semoga bermanfaat. Apa fitur pengalamatan lain yang menurut Anda berguna? Setiap penambahan, komentar, dan ulasan kode disambut dengan hangat di komentar.

Jika Anda sudah menjadi klien kami atau hanya berpikir untuk menjadi satu, pada kesempatan rilis artikel ini, kami sarankan Anda mendapatkan blok / 64 secara gratis untuk semua layanan vps atau server khusus di pusat data Equinix Tier IV, Belanda, atas permintaan ke departemen penjualan, dengan memberikan tautan ke artikel ini di tiket. Penawaran ini berlaku hingga Maret 2020.

Sedikit iklan :)


Terima kasih telah tinggal bersama kami. Apakah Anda suka artikel kami? Ingin melihat materi yang lebih menarik? Dukung kami dengan melakukan pemesanan atau merekomendasikan kepada teman Anda, VPS berbasis cloud untuk pengembang mulai $ 4,99 , analog unik dari server entry-level yang diciptakan oleh kami untuk Anda: Seluruh kebenaran tentang VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps mulai dari $ 19 atau cara membagi server? (opsi tersedia dengan RAID1 dan RAID10, hingga 24 core dan hingga 40GB DDR4).

Dell R730xd 2 kali lebih murah di pusat data Equinix Tier IV di Amsterdam? Hanya kami yang memiliki 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV dari $ 199 di Belanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - mulai dari $ 99! Baca tentang Cara Membangun Infrastruktur Bldg. kelas menggunakan server Dell R730xd E5-2650 v4 seharga 9.000 euro untuk satu sen?

Source: https://habr.com/ru/post/id484586/


All Articles