shark-vpn
以下会描述得比较通俗, 不太专业, 请专业人士见谅, 也欢迎您指正.
以下命令均需要root权限执行.
一. 首先我们要设置 Linux 的 ip_forward. 使它能在不同网卡间转发流量.
# 编辑这个文件:
vim /etc/sysctl.conf
# 将文件里的这行取消注释
# net.ipv4.ip_forward = 1
# 然后执行这条命令:
sysctl -p
# 如果有看到 net.ipv4.ip_forward = 1 说明成功了.
二. 需要新建一个虚拟网卡, 并设置它的ip, 以及设置将其流量转发到真实网卡. 一般命名为 tun0, tun1, tun2…
ip tuntap add tun0 mode tun
ip link set dev tun0 up
ifconfig tun0 192.168.194.224 netmask 255.255.255.0 promisc
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# 第一步有可能遇到如下错误:
# Object "tuntap" is unknown, try "ip help".
# 解决方法: 使用tunctl添加tun
# Unbuntu:
apt-get install uml-utilities bridge-utils
# CentOS:
yum install tunctl brctl
# 执行下面这条命令后继续第二步
tunctl -n -t tun0 -u root
三. 关键代码 (使用udp进行服务端和客户端间的数据传输)
// 打开虚拟网卡 tun0, 获取文件描述符 tun_fd
int tun_fd;
char *clonedev = "/dev/net/tun";
if ((tun_fd = open(clonedev, O_RDWR)) < 0) {
printf("error1");
return -1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "tun0", IFNAMSIZ);
int err;
if ((err = ioctl(tun_fd, TUNSETIFF, (void *) &ifr)) < 0) {
close(tun_fd);
return err;
}
uint8_t buf[MAX_PKG_LEN] = { 0 };
struct sockaddr_in* clent_addr;
// 将收到的数据写入 tun0
int n = recvfrom(udp_fd, buf, MAX_PKG_LEN, 0, (struct sockaddr*)clent_addr, &len);
write(tun_fd, buf, n);
// 读取 tun0 收到的回包并发回客户端
int n = read(tun_fd, buf, MAX_PKG_LEN);
sendto(udp_fd, buf, n, 0, clent_addr, sizeof(struct sockaddr_in));
四. 完整代码
代码完全使用 C 语言编程.
使用 epoll 进行文件描述符管理.
为了方便演示没有对数据进行加密, 在实际使用中必须加密!
一. 关键代码:
//请求打开vpn
Intent intent = VpnService.prepare(MainActivity.this);
if (intent != null) {
startActivityForResult(intent, 0);
} else {
onActivityResult(0, RESULT_OK, null);
}
//用户同意后,开启vpn服务
@SuppressLint("ResourceAsColor")
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (result == RESULT_OK) {
Intent intent = new Intent(this, MyVpnService.class);
startService(intent);
}
}
import android.net.VpnService;
public class MyVpnService extends VpnService { ... }
static String SERVER_ADDR = "192.168.1.169";
static int SERVER_PORT = 7194;
static int MAX_BKG_LEN = 65535;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Builder builder = new Builder();
builder.setSession("MyVPNService");
builder.addAddress("192.168.194.1", 24); //这个ip要和服务器的虚拟网卡ip在同一个网段
builder.addDnsServer("8.8.8.8");
builder.addRoute("0.0.0.0", 0);
static ParcelFileDescriptor mInterface = builder.establish();
FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());
InetAddress serverAddr = InetAddress.getByName(SERVER_ADDR);
DatagramSocket sock = new DatagramSocket();
sock.setSoTimeout(0); //超时为无穷大
protect(sock); //保护这个连接的数据不会进入虚拟网卡
//启动两个线程一收一发
return START_STICKY
}
//发线程
@Override
public void run() {
int length;
byte[] ip_pkg = new byte[MAX_BKG_LEN];
while ((length = in.read(ip_pkg)) >= 0) {
if (length == 0) {
continue;
}
DatagramPacket msg = new DatagramPacket(
ip_pkg, length, serverAddr, SERVER_PORT);
sock.send(msg);
}
in.close();
}
//收线程
@Override
public void run() {
byte[] ip_buf = new byte[MAX_BKG_LEN];
while (true) {
DatagramPacket msg_r = new DatagramPacket(
ip_buf, MAX_BKG_LEN, serverAddr, SERVER_PORT);
sock.receive(msg_r);
int pkg_len = msg_r.getLength();
if (pkg_len == 0) {
continue;
} else if (pkg_len < 0) {
break;
}
out.write(ip_buf, 0, pkg_len);
}
out.close();
}
// 注意: 代码里省略了很多 try catch.
二. 完整代码
这是一个完整的安卓项目
敬请期待
敬请期待