Dua jalur dapat menempatkan antarmuka IP di depan aplikasi Android, dan keduanya berakhir pada jenis deskriptor berkas yang sama dengan pemilik yang sangat berbeda. Satu melewati VpnService dan builder-nya, yang lain melalui shell root dan /dev/net/tun. Setiap pembacaan pada deskriptor itu adalah paket keluar dan setiap penulisan adalah paket masuk. Yang memisahkan kedua jalur adalah siapa yang memegang kebijakan, siapa yang bertahan dari peningkatan OS, dan berapa banyak tabel perutean yang boleh Anda sentuh.

Ambil jalur platform kecuali Anda dapat menyebut hal spesifik yang ditolaknya. Penolakan itu biasanya TAP, penangkapan di luar kumpulan aplikasi Anda sendiri, atau aturan perutean yang tidak pernah diekspos framework.

Pada jalur platform Anda menyatakan maksud alih-alih perintah. Anda menyediakan setidaknya satu alamat, rute yang diinginkan, server DNS, dan daftar aplikasi opsional, lalu layanan sistem Android melakukan penyiapan istimewa dengan identitasnya sendiri. Anda tidak pernah tahu nama antarmuka sebelumnya, dan tidak pernah mengeluarkan ioctl yang membuatnya, dan itulah intinya.

Dua cara mendapatkan antarmuka IP

Jalur root adalah resep yang lebih lama. Anda membuka /dev/net/tun, memanggil TUNSETIFF dengan IFF_TUN, menaikkan antarmuka dengan ip link, dan menambahkan alamat serta rute secara manual. Setiap langkah harus Anda lakukan dengan benar, termasuk langkah yang akan ditangani framework secara diam-diam. Satu rute yang hilang menghasilkan laporan pengguna yang sama dengan server mati.

Penelusuran kesalahan berbeda sebanyak penyiapannya. Pada jalur root antarmuka ada atau ioctl Anda mengembalikan kesalahan yang dapat Anda baca langsung. Pada jalur platform antarmuka hanya ada setelah establish() berhasil, jadi kegagalan datang sebagai pengecualian atau deskriptor null alih-alih simpul perangkat yang bisa Anda utak-atik.

Apa yang sebenarnya dikembalikan establish()

Builder.establish() memiliki tiga hasil dan ketiganya membutuhkan tanggapan berbeda. Deskriptor berarti antarmuka sudah dikonfigurasi dan milik Anda untuk digunakan, null berarti permintaan tidak lolos, dan pengecualian yang dilempar berarti layanan sistem menolaknya. Menggabungkan null dan keberhasilan ke dalam satu boolean adalah cara paling umum sebuah aplikasi melaporkan terowongan yang berfungsi yang tidak pernah membawa paket. Referensi Builder menyebutkan persyaratan alamat, dan itu layak dibaca sebelum Anda menyalahkan server.

Arah ditentukan oleh deskriptor, bukan oleh protokol Anda. Pembacaan memberi Anda paket keluar yang sudah dipilih untuk VPN, dan penulisan menyuntikkan paket masuk kembali ke antarmuka. Transport Anda sendiri yang mengenkripsi, mengautentikasi, dan membingkai paket itu, karena platform berhenti di batas paket.

Binder adalah satu lompatan, JNI adalah sumbu lain

Permintaan builder menyeberang ke sistem melalui Binder, dan di suatu tempat di bawahnya platform memanggil kode native untuk mengonfigurasi perangkat. Itu dua mekanisme berbeda yang ditumpuk dalam satu panggilan. Binder adalah komunikasi antarproses antara aplikasi Anda dan layanan sistem, sedangkan JNI adalah jembatan Java-ke-native di dalam sebuah proses. Membaca pohon AOSP Android 9 membuat tumpukan tampak literal, dan perlu diingat bahwa nama kelas internal dan jalur berkas berpindah antar rilis.

Yang tidak berpindah adalah kontrak yang boleh Anda andalkan: builder, deskriptor yang dikembalikannya, dan siklus hidup layanan di sekitarnya. Tidak ada aplikasi yang boleh memanggil transaksi layanan internal atau membuat ulang simpul perangkat untuk mendapatkan antarmuka. Jika kode Anda bergantung pada nama kelas AOSP, kode itu akan rusak pada rilis yang tidak Anda uji.

TUN membawa IP, TAP membawa Ethernet

TUN adalah antarmuka IP. TAP adalah antarmuka Ethernet, dan perbedaan itu menentukan apakah penjembatanan mungkin dilakukan. Perangkat TUN tidak memiliki alamat MAC dan tidak memiliki tajuk Ethernet untuk diteruskan, jadi klien TUN tidak dapat bergabung dengan jembatan lapisan 2, bagaimanapun rutenya ditulis.

VpnService memberi Anda varian IP, yang merupakan kebutuhan VPN tingkat aplikasi dan yang dapat didukungnya tanpa tumpukan Ethernet mentah. Jika desain Anda memerlukan penjembatanan, jalur platform tidak akan membawa Anda ke sana, dan jalur root hanya akan melakukannya jika kernel dan izin perangkat bekerja sama.

Di mana jalur root masih menang

Tiga pekerjaan mendorong orang keluar dari jalur platform. Penjembatanan lapisan 2 membutuhkan TAP. Menangkap lalu lintas dari aplikasi di luar cakupan VPN Anda membutuhkan visibilitas yang tidak diberikan framework. Perutean kebijakan yang tidak pernah diekspos builder, seperti tabel terpisah untuk satu tujuan, membutuhkan aturan yang Anda pasang sendiri.

Biayanya datang kemudian: bootloader yang tidak terkunci atau image yang di-root, kebijakan SELinux yang bergeser di bawah Anda, dan perangkat yang berhenti berperilaku benar setelah OTA. Resep yang di-root juga tidak dapat dikirim melalui distribusi aplikasi normal, jadi sebagian besar tim menyimpannya sebagai jalur diagnostik internal alih-alih produk.

Memilih di antara keduanya

Mulailah dari antarmuka yang Anda butuhkan, bukan dari kode yang Anda miliki. Jika jawabannya adalah perangkat TUN dengan rute terbatas aplikasi, jalur platform lebih pendek dan bertahan dari peningkatan. Jika jawabannya melibatkan bingkai Ethernet, penangkapan aplikasi lain, atau aturan perutean yang ditolak builder, Anda berada di wilayah root dan harus menganggarkannya sebagai biaya operasional.

Pemeriksaan yang menyelesaikan sebagian besar perdebatan: bangun jalur platform lebih dulu, catat hasil establish() terpisah dari status sesi jarak jauh, dan pastikan satu paket di setiap arah. Baru setelah itu putuskan apakah kemampuan yang hilang membenarkan meninggalkan jalur yang didukung.

Artikel terkait