شبكة VPN

يوفّر Android واجهات برمجة تطبيقات للمطوّرين لإنشاء حلول شبكات افتراضية خاصة (VPN). بعد قراءة هذا الدليل، ستعرف كيفية تطوير برنامج الشبكة الافتراضية الخاصة (VPN) واختباره للأجهزة التي تعمل بنظام التشغيل Android.

نظرة عامة

تسمح الشبكات الافتراضية الخاصة للأجهزة غير المتصلة فعليًا بأي شبكة بالوصول إلى الشبكة بأمان.

يشتمل نظام Android على برنامج شبكة VPN مدمَج (PPTP وL2TP/IPSec)، ويُسمى أحيانًا شبكة VPN القديمة. قدّم Android 4.0 (المستوى 14 لواجهة برمجة التطبيقات) واجهات برمجة تطبيقات لكي يتمكّن مطوّرو التطبيقات من توفير حلول الشبكات الافتراضية الخاصة الخاصة بهم. يمكنك تجميع حل الشبكة الافتراضية الخاصة في تطبيق يثبته الأشخاص على الجهاز. ينشئ المطورون عادةً تطبيق شبكة VPN لأحد الأسباب التالية:

  • لعرض بروتوكولات الشبكة الافتراضية الخاصة التي لا تتوافق مع البرنامج المُدمَج.
  • لمساعدة المستخدمين على الاتصال بخدمة شبكة افتراضية خاصة بدون الحاجة إلى إجراء ضبْط مُعقَّد.

يشرح الجزء المتبقي من هذا الدليل كيفية تطوير تطبيقات شبكة VPN (بما في ذلك شبكة VPN المفعّلة دائمًا ولكل تطبيق) ولا يتناول برنامج شبكة VPN المدمَج.

تجربة المستخدم

يوفّر Android واجهة مستخدم لمساعدة مستخدم في ضبط خدمة الشبكة الافتراضية الخاصة (VPN) وبدء تشغيلها وإيقافها. تجعل واجهة مستخدم النظام أيضًا الشخص الذي يستخدم الجهاز على دراية باتصال شبكة VPN نشط. يعرض Android مكونات واجهة المستخدم التالية لاتصالات الشبكة الافتراضية الخاصة:

  • قبل أن يصبح تطبيق شبكة VPN نشطًا للمرة الأولى، يعرض النظام مربع حوار طلب الاتصال. يطلب مربع الحوار من الشخص الذي يستخدم الجهاز تأكيد ثقته في شبكة VPN وقبول الطلب.
  • تعرض شاشة إعدادات الشبكة الافتراضية الخاصة (الإعدادات > الشبكة والإنترنت > شبكة VPN) تطبيقات شبكة VPN التي يقبل الشخص طلبات الاتصال بها. يوجد زر لضبط خيارات النظام أو حذف شبكة VPN.
  • تعرض شريط "الإعدادات السريعة" لوحة معلومات عندما يكون الاتصال نشطًا. يؤدي النقر على التصنيف إلى عرض مربع حوار يحتوي على مزيد من المعلومات ورابط إلى الإعدادات.
  • يشتمل شريط الحالة على رمز شبكة VPN (مفتاح) للإشارة إلى وجود اتصال نشط.

يحتاج تطبيقك أيضًا إلى توفير واجهة مستخدم حتى يتمكّن مُستخدِم الجهاز من ضبط خيارات خدمتك. على سبيل المثال، قد يحتاج حلك إلى تسجيل إعدادات مصادقة الحساب. يجب أن تعرض التطبيقات واجهة المستخدم التالية:

  • تتحكَّم هذه السياسة في بدء الاتصال وإيقافه يدويًا. شبكة VPN قيد التشغيل دائمًا: يمكن الاتصال عند الحاجة، مع السماح للأشخاص بضبط الاتصال في المرة الأولى التي يستخدمون فيها شبكة VPN.
  • يشير هذا المصطلح إلى إشعار لا يمكن تجاهله عندما تكون الخدمة نشطة. يمكن أن يعرض الإشعار حالة الاتصال أو يقدم مزيدًا من المعلومات، مثل إحصاءات الشبكة. يؤدي النقر على الإشعار إلى عرض تطبيقك في المقدّمة. أزِل الإشعار بعد أن تصبح الخدمة غير نشطة.

خدمة VPN

يربط تطبيقك شبكات النظام لمستخدم (أو ملف شخصي للعمل) ببوابة شبكة VPN. يمكن لكل مستخدم (أو ملف شخصي للعمل) تشغيل تطبيق شبكة VPN مختلف. يمكنك إنشاء خدمة VPN يستخدمها النظام لتشغيل شبكة VPN وإيقافها وتتبّع حالة الاتصال. يتم اكتساب خدمة شبكة VPN من VpnService.

تعمل الخدمة أيضًا كحاوية لاتصالات بوابة الشبكة الافتراضية الخاصة (VPN) وواجهات الأجهزة المحلية الخاصة بها. من خلال استدعاء مثيل الخدمة VpnService.Builder، يمكن إنشاء واجهة محلية جديدة.

الشكل 1. كيفية ربط VpnService بشبكات Android ببوابة شبكة VPN
مخطّط لبنية الكتلة يوضّح كيف تنشئ فئة VpnService واجهة TUN محلية في شبكات النظام

ينقل تطبيقك البيانات التالية لتوصيل الجهاز بمدخل شبكة VPN:

  • يقرأ حزم IP الصادرة من واصف الملفات للواجهة المحلية، ويشفّرها ويرسلها إلى بوابة الشبكة الافتراضية الخاصة.
  • تكتب الحِزم الواردة (التي تم استلامها وفك تشفيرها من بوابة الشبكة الافتراضية الخاصة) في واصف الملفات للواجهة المحلية.

ولا تتوفّر سوى خدمة نشطة واحدة لكل مستخدم أو ملف شخصي. يؤدي بدء خدمة جديدة إلى إيقاف الخدمة الحالية تلقائيًا.

إضافة خدمة

لإضافة خدمة شبكة VPN إلى تطبيقك، عليك إنشاء خدمة Android اكتسابها من VpnService. تعريف خدمة شبكة VPN في ملف بيان التطبيق مع الإضافات التالية:

  • عليك حماية الخدمة باستخدام إذن BIND_VPN_SERVICE حتى لا يتمكن سوى النظام فقط من الربط بخدمتك.
  • أعلِن عن الخدمة باستخدام فلتر الأهداف "android.net.VpnService" حتى يتمكّن النظام من العثور على خدمتك.

يوضّح هذا المثال كيفية تعريف الخدمة في ملف بيان التطبيق:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
</service>

بعد أن أعلن تطبيقك عن اسم الخدمة، يمكن للنظام بدء تشغيل خدمة VPN في تطبيقك وإيقافها تلقائيًا عند الحاجة. على سبيل المثال، يتحكّم النظام في خدمتك عند تشغيل شبكة VPN قيد التشغيل دائمًا.

إعداد الخدمة

لإعداد التطبيق ليصبح خدمة شبكة VPN الحالية للمستخدم، اتصل بالرقم VpnService.prepare(). إذا لم يمنح الشخص الذي يستخدم الجهاز إذنًا لتطبيقك بالفعل، فإن الطريقة تُرجع الغرض من النشاط. ويمكنك استخدام هذا الغرض لبدء نشاط نظام يطلب الإذن. يعرض النظام مربّع حوار مشابهًا لمربّعات حوار الأذونات الأخرى، مثل الوصول إلى الكاميرا أو جهات الاتصال. وإذا كان تطبيقك جاهزًا، ستعرض الطريقة null.

يمكن أن يكون تطبيق واحد فقط هو خدمة شبكة VPN المعدّة حاليًا. يمكنك الاتصال دائمًا بالرقم VpnService.prepare() لأنه ربما يكون أحد الأشخاص قد ضبط تطبيقًا مختلفًا كخدمة شبكة VPN منذ آخر مرة استدعى فيها تطبيقك الطريقة. لمعرفة المزيد، راجع قسم دورة حياة الخدمة.

ربط خدمة

بعد تشغيل الخدمة، يمكنك إنشاء واجهة محلية جديدة مرتبطة ببوابة الشبكة الافتراضية الخاصة. لطلب الإذن والاتصال بخدمتك بمدخل الشبكة الافتراضية الخاصة (VPN)، عليك إكمال الخطوات بالترتيب التالي:

  1. اتصل بـ VpnService.prepare() لطلب الإذن (عند الحاجة).
  2. يمكنك الاتصال بـ VpnService.protect() للاحتفاظ بمقبس النفق لتطبيقك خارج شبكة VPN للنظام وتجنُّب الاتصال الدائري.
  3. يمكنك الاتصال بـ DatagramSocket.connect() لتوصيل نفق تطبيقك ببوابة الشبكة الافتراضية الخاصة.
  4. يمكنك استدعاء طرق VpnService.Builder لضبط واجهة TUN محلية جديدة على الجهاز لحركة بيانات الشبكة الافتراضية الخاصة.
  5. استدعِ VpnService.Builder.establish() حتى ينشئ النظام واجهة TUN المحلية ويبدأ في توجيه حركة البيانات من خلال الواجهة.

تقترح بوابة الشبكة الافتراضية الخاصة عادةً إعدادات لواجهة TUN المحلية أثناء المصافحة. يستدعي تطبيقك طرق VpnService.Builder لإعداد خدمة كما هو موضح في النموذج التالي:

Kotlin

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
val builder = Builder()

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
val localTunnel = builder
        .addAddress("192.168.2.2", 24)
        .addRoute("0.0.0.0", 0)
        .addDnsServer("192.168.1.1")
        .establish()

Java

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
VpnService.Builder builder = new VpnService.Builder();

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
ParcelFileDescriptor localTunnel = builder
    .addAddress("192.168.2.2", 24)
    .addRoute("0.0.0.0", 0)
    .addDnsServer("192.168.1.1")
    .establish();

يعرض المثال في قسم شبكة VPN حسب التطبيق إعدادات IPv6، بما في ذلك المزيد من الخيارات. عليك إضافة قيم VpnService.Builder التالية قبل إنشاء واجهة جديدة:

addAddress()
أضِف عنوان IPv4 أو IPv6 واحدًا على الأقل مع قناع شبكة فرعية يضبطه النظام كعنوان لواجهة TUN المحلية. يتلقّى تطبيقك عادةً عناوين IP وأقنعة الشبكة الفرعية من بوابة شبكة VPN أثناء المصافحة.
addRoute()
أضِف مسارًا واحدًا على الأقل إذا كنت تريد أن يرسل النظام حركة المرور عبر واجهة شبكة VPN. تتم فلترة المسارات حسب عناوين الوجهات. لقبول جميع كثافة حركة المرور، حدِّد مسارًا مفتوحًا مثل 0.0.0.0/0 أو ::/0.

تعرض الطريقة establish() مثيل ParcelFileDescriptor الذي يستخدمه تطبيقك لقراءة الحزم وكتابتها من المخزن المؤقت للواجهة ومنه. تعرض الطريقة establish() null إذا لم يكن تطبيقك مُعدًّا أو إذا أبطل أحد المستخدمين الإذن.

مراحل نشاط الخدمة

يجب أن يتتبّع تطبيقك حالة شبكة VPN التي اختارها النظام وأي اتصالات نشطة. عليك تحديث واجهة المستخدم في التطبيق لإبقاء مستخدم الجهاز على علم بأي تغييرات يتم إجراؤها.

بدء خدمة

يمكن بدء خدمة شبكة VPN بالطرق التالية:

ويشغّل تطبيقك خدمة شبكة VPN من خلال تمرير هدف إلى startService(). وللتعرّف على مزيد من المعلومات، يُرجى الاطّلاع على بدء خدمة.

يبدأ النظام الخدمة في الخلفية من خلال الاتصال بالرقم onStartCommand(). ومع ذلك، يفرض Android قيودًا على تطبيقات الخلفية في الإصدار 8.0 (مستوى واجهة برمجة التطبيقات 26) أو الإصدارات الأحدث. إذا كنت تتيح مستويات واجهة برمجة التطبيقات هذه، عليك نقل خدمتك إلى المقدّمة عن طريق طلب Service.startForeground(). لمزيد من المعلومات، يُرجى الاطّلاع على تشغيل خدمة في المقدّمة.

إيقاف خدمة

يمكن لشخص يستخدم الجهاز إيقاف الخدمة عن طريق استخدام واجهة مستخدم التطبيق. أوقِف الخدمة بدلاً من إغلاق الاتصال فقط. يوقف النظام أيضًا اتصالاً نشطًا عندما ينفّذ مستخدم الجهاز ما يلي في شاشة شبكة VPN في تطبيق "الإعدادات":

  • إلغاء ربط تطبيق شبكة VPN أو نسيانه
  • لإيقاف شبكة VPN التي تعمل دائمًا للاتصال النشط

يستدعي النظام طريقة onRevoke() الخاصة بخدمتك، لكن قد لا يحدث هذا الطلب في سلسلة التعليمات الرئيسية. عندما يستدعي النظام هذه الطريقة، تكون واجهة الشبكة البديلة توجه حركة البيانات بالفعل. يمكنك التخلص من الموارد التالية بأمان:

  • عليك إغلاق مقبس النفق المحمي ببوابة الشبكة الافتراضية الخاصة من خلال طلب الرقم DatagramSocket.close().
  • أغلِق واصف ملف الطرد (لست بحاجة إلى استنزافه) من خلال طلب الرمز ParcelFileDescriptor.close().

شبكة VPN قيد التشغيل دائمًا

يمكن لنظام Android تفعيل خدمة شبكة VPN عند تشغيل الجهاز وإبقاءه قيد التشغيل أثناء تشغيل الجهاز. ويُطلَق على هذه الميزة اسم شبكة VPN قيد التشغيل دائمًا وتتوفّر في الإصدار Android 7.0 (مستوى واجهة برمجة التطبيقات 24) والإصدارات الأحدث. بالرغم من احتفاظ Android بدورة حياة الخدمة، فإن خدمة الشبكة الافتراضية الخاصة (VPN) هي المسؤولة عن اتصال بوابة شبكة VPN. ويمكن أيضًا لشبكة VPN التي يتم تشغيلها دائمًا حظر الاتصالات التي لا تستخدم هذه الشبكة.

تجربة المستخدم

في الإصدار Android 8.0 أو الإصدارات الأحدث، يعرض النظام مربّعات الحوار التالية لإعلام مستخدِم الجهاز بشبكة VPN التي يتم تشغيلها دائمًا:

  • عندما ينقطع الاتصال باتصالات شبكة VPN التي يتم تشغيلها دائمًا أو يتعذّر الاتصال بها، يظهر للمستخدمين إشعار لا يمكن إغلاقه. يؤدي النقر على الإشعار إلى إظهار مربع حوار يشرح المزيد. يختفي الإشعار عندما تعاود شبكة VPN الاتصال بالإنترنت أو عندما يوقف أحد الأشخاص خيار تشغيل شبكة VPN بشكل دائم.
  • تسمح شبكة VPN التي يتم تشغيلها دائمًا للشخص الذي يستخدم أي جهاز بحظر أي اتصالات بالشبكة لا تستخدم شبكة VPN. عند تفعيل هذا الخيار، يحذّر تطبيق "الإعدادات" الأشخاص من عدم توفّر اتصال بالإنترنت قبل الاتصال بشبكة VPN. يطلب تطبيق الإعدادات من الشخص الذي يستخدم الجهاز المتابعة أو الإلغاء.

نظرًا لأنّ النظام (وليس من قِبل شخص) يبدأ الاتصال الدائم ويوقفه، ستحتاج إلى تعديل سلوك تطبيقك وواجهة المستخدم عليه:

  1. عليك إيقاف أي واجهة مستخدم تلغي الاتصال، لأنّ النظام وتطبيق "الإعدادات" يتحكّمان في الاتصال.
  2. احفظ أي تكوين بين بدء تشغيل كل تطبيق واضبط عملية الربط بأحدث الإعدادات. نظرًا لأن النظام يبدأ تشغيل تطبيقك عند الطلب، فقد لا يرغب الشخص الذي يستخدم الجهاز دائمًا في تهيئة اتصال.

يمكنك أيضًا استخدام عمليات الضبط المُدارة لضبط اتصال. تساعد عمليات الضبط المُدارة مشرف تكنولوجيا المعلومات في إعداد شبكة VPN عن بُعد.

الرصد قيد التشغيل دائمًا

لا يتضمّن نظام Android واجهات برمجة تطبيقات للتأكّد مما إذا كان النظام قد بدأ تشغيل خدمة شبكة VPN. ولكن عندما يُبلغ تطبيقك عن أي حالات خدمة عند بدء تشغيلها، يمكنك افتراض أن النظام بدأ في تشغيل خدمات لم يتم وضع علامة عليها لشبكات VPN التي يتم تشغيلها دائمًا. وفي ما يلي مثال على ذلك:

  1. أنشئ مثيل Intent لبدء خدمة شبكة VPN.
  2. أبلِغ عن خدمة شبكة VPN من خلال إضافة رمز إضافي إلى الغرض.
  3. في طريقة onStartCommand() للخدمة، ابحث عن العلامة في الإضافات الإضافية للوسيطة intent.

الاتصالات المحظورة

يمكن لأي شخص يستخدم الجهاز (أو أحد مشرفي تكنولوجيا المعلومات) أن يفرض على جميع الزيارات استخدام شبكة VPN. يحظر النظام أي حركة بيانات للشبكة لا تستخدم شبكة VPN. ويمكن لمستخدمي الجهاز العثور على مفتاح التبديل حظر أي اتصالات بدون استخدام شبكة VPN في لوحة خيارات شبكة VPN في "الإعدادات".

إيقاف الإعداد "قيد التشغيل دائمًا"

إذا لم يكن تطبيقك يتيح حاليًا استخدام شبكة VPN التي يتم تشغيلها دائمًا، يمكنك إيقافها (في الإصدار 8.1 من نظام التشغيل Android أو الإصدارات الأحدث) من خلال ضبط البيانات الوصفية لخدمة SERVICE_META_DATA_SUPPORTS_ALWAYS_ON على false. يوضح مثال بيان التطبيق التالي كيفية إضافة عنصر البيانات الوصفية:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
     <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
             android:value=false/>
</service>

عندما يوقف تطبيقك شبكة VPN التي يتم تشغيلها دائمًا، يوقف النظام خيارات عناصر التحكّم في واجهة المستخدم من "الإعدادات".

استخدام شبكة افتراضية خاصة حسب التطبيق

يمكن لتطبيقات الشبكة الافتراضية الخاصة فلترة التطبيقات المثبّتة المسموح لها بإرسال حركة البيانات من خلال اتصال شبكة VPN. يمكنك إنشاء قائمة مسموح بها أو قائمة غير مسموح بها، ولكن ليس كليهما. إذا لم تنشئ قوائم مسموحًا بها أو غير مسموح بها، سيرسل النظام جميع حركة بيانات الشبكة من خلال شبكة VPN.

يجب أن يتولّى تطبيق شبكة VPN إعداد القوائم قبل إنشاء الاتصال. إذا كنت بحاجة إلى تغيير القوائم، أنشئ اتصالاً جديدًا عبر شبكة VPN. يجب تثبيت التطبيق على الجهاز عند إضافته إلى قائمة.

Kotlin

// The apps that will have access to the VPN.
val appPackages = arrayOf(
        "com.android.chrome",
        "com.google.android.youtube",
        "com.example.a.missing.app")

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
val builder = Builder()
for (appPackage in appPackages) {
    try {
        packageManager.getPackageInfo(appPackage, 0)
        builder.addAllowedApplication(appPackage)
    } catch (e: PackageManager.NameNotFoundException) {
        // The app isn't installed.
    }
}

// Complete the VPN interface config.
val localTunnel = builder
        .addAddress("2001:db8::1", 64)
        .addRoute("::", 0)
        .establish()

Java

// The apps that will have access to the VPN.
String[] appPackages = {
    "com.android.chrome",
    "com.google.android.youtube",
    "com.example.a.missing.app"};

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
VpnService.Builder builder = new VpnService.Builder();
PackageManager packageManager = getPackageManager();
for (String appPackage: appPackages) {
  try {
    packageManager.getPackageInfo(appPackage, 0);
    builder.addAllowedApplication(appPackage);
  } catch (PackageManager.NameNotFoundException e) {
    // The app isn't installed.
  }
}

// Complete the VPN interface config.
ParcelFileDescriptor localTunnel = builder
    .addAddress("2001:db8::1", 64)
    .addRoute("::", 0)
    .establish();

التطبيقات المسموح بها

لإضافة تطبيق إلى القائمة المسموح بها، يمكنك الاتصال بالرقم VpnService.Builder.addAllowedApplication(). إذا كانت القائمة تتضمن تطبيقًا واحدًا أو أكثر، ستعمل التطبيقات المُدرَجة في القائمة فقط على استخدام شبكة VPN. وتستخدم جميع التطبيقات الأخرى (غير المدرَجة في القائمة) شبكات النظام كما لو كانت شبكة VPN لا تعمل. عندما تكون قائمة التطبيقات المسموح بها فارغة، تستخدم جميع التطبيقات شبكة VPN.

التطبيقات غير المسموح بها

لإضافة تطبيق إلى قائمة التطبيقات غير المسموح بها، يمكنك الاتصال بالرقم VpnService.Builder.addDisallowedApplication(). تستخدم التطبيقات غير المسموح بها شبكات النظام كما لو كانت شبكة VPN لا تعمل، وجميع التطبيقات الأخرى تستخدم شبكة VPN.

تجاوز شبكة VPN

يمكن لشبكتك الافتراضية الخاصة السماح للتطبيقات بتجاوز شبكة VPN واختيار الشبكة الخاصة بها. لتجاوز شبكة VPN، يمكنك استدعاء VpnService.Builder.allowBypass() عند إنشاء واجهة VPN. لا يمكنك تغيير هذه القيمة بعد بدء تشغيل خدمة شبكة VPN. إذا لم يربط أحد التطبيقات العملية أو المقبس بشبكة معينة، ستستمر حركة بيانات الشبكة في التطبيق عبر شبكة VPN.

لا يتوفّر اتصال للتطبيقات المرتبطة بشبكة معيّنة عندما يحظر أحد المستخدمين حركة مرور لا تمر عبر شبكة VPN. لإرسال حركة البيانات عبر شبكة معيّنة، تستخدم التطبيقات طرق الاتصال، مثل ConnectivityManager.bindProcessToNetwork() أو Network.bindSocket() قبل توصيل المقبس.

نموذج التعليمات البرمجية

يتضمّن مشروع Android المفتوح المصدر نموذجًا لتطبيق يُسمى ToyVPN. يعرض هذا التطبيق كيفية إعداد خدمة VPN وربطها.