العمل مع IPv6 في PHP

لقد تلقينا مؤخرًا حالة LIR و / 29 كتلة IPv6. ثم كانت هناك حاجة لتتبع الشبكات الفرعية المعينة. ونظرًا لأن الفواتير الخاصة بنا تمت كتابتها بلغة PHP ، كان عليّ أن أستلهم بعض الشيء من هذه القضية وأدرك أن هذه اللغة ليست الأكثر ودية من حيث العمل مع IPv6. تحت خفض - حلنا لمشاكل العمل مع عناوين ونطاقات. ربما ليست الأكثر أناقة ، لكنها تؤدي المهام.



قليلا من الناحية النظرية


تنويه. إذا كنت معتادًا على ماهية IPv6 وماهية تناوله ، فقد يكون هذا الجزء مملًا بالنسبة لك. قد لا يكون.

قد يتم تثبيط الأشخاص الذين رأوا أول تعليق توضيحي لـ IPv6. بعد 64.233.177.101 الأنيق ، صادفنا فجأة 2607: f8b0: 4002: c08 :: 8b ويمكن أن نتشوش . كل من هذا ، وآخر - تمثيل الإنسان فقط للقراءة من 32 و 128 بت على التوالي. تحتوي أي حزمة IP على رأس بتخصيص موحد تمامًا لكل بت. دون الخوض في بنية الرؤوس ، نحتاج إلى الحصول على شيء واحد من هنا: بالنسبة للعمليات مع عناوين IP ونطاقاتها ، من الملائم عمومًا استخدام العمليات الثنائية للرياضيات والثنائية الاتجاه. كما أنه من الأنسب تخزينها في قاعدة البيانات كـ BINARY (4) لـ IPv4 و BINARY (16) لـ IPv6.

جانب هام آخر يجب معالجته هو أقنعة الشبكة وتدوين CIDR. CIDR هو اختصار لـ Classless Inter-Domain Routing. استبدل هذا المفهوم الفئة الأولى في مسألة تحديد أي جزء من عنوان IP هو بادئة الشبكة وأي جزء هو عنوان واجهة الشبكة داخل هذه الشبكة. في الممارسة العملية ، سيتم تعيين البتات n الأولى المقابلة للبادئة على 1 ، والباقي على 0.

في شكل مقروء من قبل الإنسان ، تتم كتابة هذا كـ ip.add.re.ss. / cidr . على سبيل المثال ، 64.233.177.0/24 يعني أن أول 24 بت تشير إلى البادئة. آخر 8 بتات ، وهي الرقم الأخير في إدخال يمكن قراءته بواسطة الإنسان ، يرجى الرجوع إلى العنوان الموجود داخل الشبكة الفرعية. تمارين أكثر زوجين. 64.233.177.101/32 و 2607: f8b0: 4002: c08 :: 8b / 128 - عنوان واحد محدد. 2607: f8b0: 4002: c08 :: / 64 - أول 64 بت (أول 4 مجموعات) - البادئة ، 64 بت المتبقية - الجزء المحلي. بالمناسبة ، إذا كان أي شخص محرجًا من علامة "::" في الإدخال ، فإن القولون المزدوج يستبدل عددًا تعسفيًا من المقاطع التي تحتوي على 0. يمكن أن يحدث في التعليق التوضيحي مرة واحدة فقط. بمعنى آخر ، 2607: f8b0: 4002: c08 :: 8b = 2607: f8b0: 4002: c08: 0: 0: 0: 8b .

ماذا نحتاج أن نتعلم من كل هذا؟ أولاً ، يمكن الحصول على عناوين الشبكات الفرعية الأولى والأخيرة باستخدام ثنائي AND و OR ، مع العلم القناع في شكل ثنائي. ثانياً ، يمكن حساب الشبكة الفرعية التالية من الحجم (بمعنى CIDR) n بإضافة 1 إلى الموضع التاسع في التمثيل الثنائي. عن طريق العرض الثنائي ، أعني نتيجة استخدام الدالتين pack () و inet_pton () والاستخدام الإضافي لمشغلي bitwise ، من خلال binary - تمثيل في النظام الثنائي ، والذي يمكن الحصول عليه ، على سبيل المثال ، باستخدام base_convert () .

الخلفية التاريخية
الفصل الطبقي للعنونة السابقة. في تلك السنوات البعيدة ، لم يتوقع أحد أن يكون هناك الكثير من الشبكات الفرعية ، تم توزيعها على اليمين واليسار بواسطة كتل كبيرة: الفئة A - أول 8 بت (أي الرقم الأول) كانت مسبوقة ، مع البتة البادئة 0 ؛ الفئة ب - أول 16 (أول رقمين) ، البتات الرائدة من 10 ؛ الفئة C - أول 24 بت ، البتات الرائدة 110. تحدد هذه البتات البادئة نفس النطاقات التي تم إصدار عنوان الفئة فيها: 0.0.0.0 - 127.255.255.255 للفئة A ، 128.0.0.0 - 191.255.255.255 - الفئة B ، 192.0 .0 - 223.255.255.255 - فئة C. مع انتشار الإنترنت في جميع أنحاء الكوكب ، أدرك المنظمون أنهم قد فاتتهم ، وفي أوائل التسعينات طوروا مفهومًا غير طبقي ، مما سمح لهم بعدم الانضمام إلى البتات الرئيسية. يمكن العثور على مزيد من التفاصيل ، على سبيل المثال ، في كل شيء عظيم المعرفة .


دعنا ننتقل إلى الممارسة


في الممارسة العملية ، ننفذ المهام الثلاث الأكثر احتمالًا ، كما بدا لي ،:

  1. الحصول على العنوان الأول والأخير للنطاق ؛
  2. الحصول على النطاق التالي من حجم معين (CIDR) ؛
  3. التحقق من أن العنوان ينتمي إلى مجموعة.

سيكون التنفيذ لـ IPv6 ، لكن إذا لزم الأمر ، يمكن تكييف المنطق بسهولة. حصلت على بعض الأفكار من هنا ، لكنني نفذت بطريقة مختلفة قليلاً. أيضا في الأمثلة لا يوجد التحقق من أخطاء الإدخال. لذلك دعونا نذهب.

كما ذكرت ، يمكن تحديد العنوان الأول والأخير لنطاق باستخدام عمليات bitwise ، مع العلم بداية النطاق وقناع الشبكة الفرعية الثنائية. وفقًا لذلك ، فإن أول ما نحتاج إلى فعله هو تحويل CIDR إلى قناع ثنائي. للقيام بذلك ، اجمع تمثيله السداسي وقم بتعبئته في ملف ثنائي.

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

حزمة الاستدعاء ('H *' ، قناع $) تحزم تمثيل hex بنفس طريقة inet_pton () . الاختلاف الوحيد هو أنه عندما يتم استدعاء pack () ، يجب أن تكون الكل 0 في مكانها ، ويجب ألا يكون هناك نقطتين في الإدخال ، بخلاف الإدخال القابل للقراءة من قبل الإنسان.

الخطوة التالية هي حساب بداية ونهاية النطاق. وهنا هناك فروق دقيقة. عمليات Bitwise محدودة بسعة المعالج. وفقًا لذلك ، على CubieTruck 32 بت الخاصة بي ، والتي أستخدمها أحيانًا لأي تدليل للاختبار ، لا يمكن معالجة جميع 128 بت من العنوان في عملية واحدة. ومع ذلك ، لا يوجد شيء يمنعنا من تقسيمها إلى مجموعات مكونة من 32 بتة (فقط في حالة معرفة من هو المعالجات التي سنقوم بتشغيلها).

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

للاستخدام في المستقبل ، سوف نقدم القدرة على نقل IP والحصول على النتيجة سواء في شكل ثنائي وقراءة الإنسان. تحدد المعلمة $ التي تحدد هنا ما إذا كنا نريد الحصول على بداية أو نهاية النطاق (القيم هي "بداية" أو "نهاية" ، على التوالي).

المهمة التالية (إلى جانب الأكثر عملية لشركتنا) هي حساب النطاق التالي. بالنسبة لهذه المهمة ، لم يتبادر إلى الذهن أي شيء أفضل ، باستثناء كيفية تحليل العنوان في سلسلة ثنائية وإضافة 1 في الموضع المطلوب ، ثم طي كل شيء مرة أخرى. لمنع القطع الأثرية من الظهور في أي مكان ، قررت تقسيم العنوان بالبايتة أثناء التحلل والتجميع.

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

في الإخراج ، نحصل على بادئة نطاق الحجم التالي المحدد في $ cidr . باستخدام هذه الوظيفة ، نخصص مجموعات عناوين لعملائنا.

أخيرًا ، تحقق مما إذا كان العنوان ينتمي إلى النطاق. على سبيل المثال ، خصصنا كتلة واحدة / 48 لتوزيع / 64 كتلة للعملاء ، وعلينا التأكد من أننا خلال الموعد لا نتجاوز الكتلة المخصصة (في الواقع ، سيحدث هذا قريبًا ، ولكن لا تزال هناك فرصة). كل شيء بسيط هنا. نحصل على بداية ونهاية النطاق في شكل ثنائي ونتحقق مما إذا كان العنوان موجودًا.

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

آمل أن يكون مفيدا. ما هي ميزات العنونة الأخرى التي قد تجدها مفيدة؟ أي إضافات وتعليقات ومراجعات الكود مرحب بها بحرارة في التعليقات.

إذا كنت بالفعل عميلًا لدينا أو كنت تفكر في أن تصبح عميلًا ، بمناسبة إصدار هذه المقالة ، فنحن نقترح عليك الحصول على الكتلة / 64 مجانًا لجميع خدمات vps أو خادم مخصص في مركز بيانات Equinix Tier IV ، هولندا ، بناءً على طلب إلى قسم المبيعات ، من خلال توفير رابط إلى هذا المقال في التذكرة. العرض ساري حتى مارس 2020.

قليلا من الإعلان :)


شكرا لك على البقاء معنا. هل تحب مقالاتنا؟ تريد أن ترى المزيد من المواد المثيرة للاهتمام؟ ادعمنا عن طريق تقديم طلب أو التوصية لأصدقائك ، VPS المستندة إلى مجموعة النظراء للمطورين من 4.99 دولار ، وهو تناظرية فريدة من الخوادم على مستوى الدخول التي اخترعناها لك: الحقيقة الكاملة حول VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps من 19 $ أو كيفية تقسيم الخادم؟ (تتوفر خيارات مع RAID1 و RAID10 ، ما يصل إلى 24 مركزًا وما يصل إلى 40 جيجابايت من ذاكرة DDR4).

Dell R730xd أرخص مرتين في مركز بيانات Equinix Tier IV في أمستردام؟ فقط لدينا 2 من Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 جيجا هرتز 14 جيجا بايت 64 جيجا بايت DDR4 4 × 960 جيجا بايت SSD 1 جيجابت في الثانية 100 TV من 199 دولار في هولندا! Dell R420 - 2x E5-2430 سعة 2 جيجا هرتز 6 جيجا بايت 128 جيجا بايت DDR3 2x960GB SSD بسرعة 1 جيجابت في الثانية 100 تيرابايت - من 99 دولارًا! اقرأ عن كيفية بناء البنية التحتية فئة باستخدام خوادم V4 R730xd E5-2650d تكلف 9000 يورو عن بنس واحد؟

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


All Articles