Android ToyVpn by native code

今兒個練習使用 Android 的 VpnService,為了能使用手邊現有的 C++ 程式碼,需要使用 NDK 來寫原生程式。弄一弄覺得在 Java 與 C 之間切換實在有夠麻煩,索性把 Android SDK 裡面附的 ToyVpn 改用 C 語言重寫一次。Java 部份就只負責 UI 的互動,與一些初始化的動作而已,其餘的部份都交給 C 去執行,應該效能也會更好(吧?)。

大致上運作方式都沒有變,傳輸也依然沒有加密,使用者密碼也還是寫死狀態。
只加上了一個 Keep Alive 的功能,讓它閒置一段時間也不會斷線。

完整的程式碼在 GitHub:
https://github.com/weichenlin/NativeToyVpn

2013/12/20 更新:
增加了一個支援多 client 的 server
主要差異在於需要給每個 client 不同的 ip 做區別
另外還需要稍微分析一下封包的位址才能分辨到底是要給誰的
程式碼一樣放在 GitHub

[參考資料]
一篇很好的 tun/tap 入門教學:http://backreference.org/2010/03/26/tuntap-interface-tutorial/
比較詳細的運作原理:http://www.ibm.com/developerworks/cn/linux/l-tuntap/index.html
tcp/ip stack:https://github.com/saminiir/level-ip

在〈Android ToyVpn by native code〉中有 23 則留言

  1. 你好~ 我用了你的nativetoypvn的程式,但是我一打開它設定了 server address ,port and key 一點 connet鍵之後他會立刻出現”native toy vpn已停止” 但是我之前使用toyvpn並不會發生這種情形 ,想要請問這會是甚麼可能的問題?! 謝謝你的回覆

  2. 这个工程,对于需要用户名和密码验证的vpn服务器,不支持的吧?我最近也遇到这个问题,还希望作者能给予指点。

    • 如其名,這只是個玩具等級的 VPN,用意在示範如何讓 Android 搭建起一個 VPN 連線。用戶驗證其實不難做,一開始先傳輸身份驗證的資料即可。其它一些基本的功能應該也都不困難,真正需要功夫的是加密傳輸。少了加密的 VPN,真的就只能當成玩具玩玩。

      • 同意你的观点。目前在安卓上,还是有几款不错的vpn软件,大陆也有比较好的supervpn。最近我也在琢磨写个这样的小程序娱乐,无奈对于网络这里的知识了解甚少。我知道无论是协议,还是加密算法,都是有现成的代码可以使用。问题在于:我对这整个流程还不太清晰,比如,按照你的方式,所有的数据操作,都放在jni里面,那这里是否需要自己去建立隧道?还有就是,在连接到vpn服务之前,必然有一个验证方式以确认这个连接是受信任的,这是否意味着,我只要把账号密码,按照对应的加密协议加密发送过去就ok了呢?

        • 建立 VPN 的步驟大致上是:
          1. 讓系統將網路流量都交給 VPN Client 處理,因為 API 的限制,這得在 Java 做比較方便。
          2. VPN Client 獲取了這些封包,得送到遠端的 VPN Server 去,這邊就包含了網路傳輸,身份驗證,Server 驗證,加密,通訊協定等。
          3. VPN Server 收到了 VPN Client 傳來的封包,需要為其建立 NAT,並開始傳輸數據。
          4. VPN Client 得到 VPN Server 返回的封包,將其解密,交還給系統產生的 VPN 界面,最終讓資料到達各個 App。

          除了第一步驟是 Java API,其餘都可以在 JNI 完成。你想用 Java 寫就全用 Java,用 JNI 則是為了省電與效率。沒什麼好理由就別摻雜 Java 與 JNI,反而會讓整個邏輯變得複雜難懂,沒有好處。

          驗證的部份我會建議你先找密碼學的書讀一讀。基本上並不是只有用戶需要被驗證,你還得驗證你連線的 Server 有沒有被劫持。根據你想達成的功能(想要翻牆?偽裝?)與安全性(避免重送攻擊?),還會有各種各樣的問題需要解決。而且常見的加密協定很多是給 TCP 用的,然而 VPN 通常走 UDP 協定比較適合,資料就比較少了。加密與安全性這邊要考慮的很多,三言兩語講不完,最好能一邊看書一邊找 open source 的專案研究學習。

          • 十分感谢你的耐心回复。我找到了一篇文章,介绍安卓网络管理机制的,随后我按照系统设置vpn的流程,去了解了一下。既然安卓有设置vpn的功能,那肯定也有对应的接口提供给开发者,我尝试去做做,如果弄好了,就共享出去。再次感谢!

  3. 在toyvpn中的Android客户端里我需要输入(server IP,server port,shared secret,http proxy server,http proxy port,package)参数,而在server中需要使用./ToyVpnServer tun0 8000 -m 1400 -a 10.0.0.2 32 -d 8.8.8.8 -r 0.0.0.0 0。我想问的是客服端要输入什么才可以可服务器端进行链接呢?两者的参数分别如何对应呢?希望得到您的回复!感谢!

    • 如果你是用我 github 上面的程式, 應該只會跟你要 ip, port, secret, 那個 ip 就是你 server 的 ip, port 你是用 8000, secret 你應該要加在 ToyVpnServer 的參數上面, ToyVpnServer 第三個參數就是 secret

      • 感谢您的回复.
        我之前在运行的是toyvpn的样例,关于toyvpn是否只需要在服务器设置好port和secret,然后在客户端运行之后输入serverip,serverport,secret即可?
        另,我看了看您的代码,您是用C++来实现官方样例中的connection部分?

      • 我看到您还实现了支持多个客户端的服务器,我是刚刚入门这一方面,可否和您请教这一部分的原理?感谢!

  4. 我看到您还实现了支持多个客户端的服务器,我是刚刚入门这一方面,可否和您请教这一部分的原理?感谢!

    • 官方給的範例是沒有區分客戶端的, 它收到封包就往 tun 送, tun 回應了什麼就往客戶端發

      我這個改動也沒什麼複雜的, 客戶端會有兩組 ip/port, 一個是手機連網用的, 一個是 vpn 服務器發的, 就是在服務器收到封包以後, 檢查一下 ip/port 去找出對應的客戶端 ip/port, 然後發回去, 做的還是挺簡陋的, 應該不難理解

      且這作法無法應對手機因為移動時造成的 ip 變更, 因此只要 ip 一變就得從新連服務器

      • 感谢您的耐心回复,我已经down了您的代码,但是在服务器端和客户端运行出现了问题.
        在客户端jni的配置和android.mk应该如何去运行?
        在服务器端您的多客户端版本里有多个cpp文件,需要用makefile来进行编译吗?
        感谢!

      • 我已经分别运行起了linux-服务器端和Android客户端,但是在服务器端出现的log信息总是
        WARN: got packet for unknown ip
        —-0.0.0.0:24576 -> 117.74.85.44:0
        但是我本机和服务器的ip都不是117.74.85.44,这是为什么?
        而在客户端一直是nativevpn is connecting.我是用ssh来连接服务器,在本机用ping可以ping到服务器的ip,请问上面现象的原因是什么?是需要关闭防火墙?正确的服务器端和客户端的显示应该是如何?
        期待您的回复!感谢!

        • 我不確定你有沒有按照註解的內容去設定好 server, 如果成功連線應該會有一行 “got new client”

          ToyVpnServer_MU.cpp 大約 42 行開始的註解是關於要怎樣設定 server 的, 你看看有沒有漏的

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *