Table of Contents
VPN TUN
-
应用程序A是一个普通的程序,通过socket A发送了一个数据包,假设这个数据包的目的IP地址是192.168.3.1
-
socket将这个数据包丢给协议栈
-
协议栈根据数据包的目的IP地址,匹配本地路由规则,知道这个数据包应该由tun0出去,于是将数据包交给tun0
-
tun0收到数据包之后,发现另一端被进程B打开了,于是将数据包丢给了进程B
-
进程B收到数据包之后,做一些跟业务相关的处理,然后构造一个新的数据包,将原来的数据包嵌入在新的数据包中,最后通过socket B将数据包转发出去,这时候新数据包的源地址变成了eth0的地址,而目的IP地址变成了一个其它的地址,比如是10.33.0.1.
-
socket B将数据包丢给协议栈
-
协议栈根据本地路由,发现这个数据包应该要通过eth0发送出去,于是将数据包交给eth0
-
eth0通过物理网络将数据包发送出去
TUN编程
源代码
/**
* TUN/TAP编程示例
*/
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <linux/if_tun.h>
#include <stdlib.h>
#include <stdio.h>
int tun_alloc(int flags)
{
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
//打开tun设备
if ((fd = open(clonedev, O_RDWR)) < 0) {
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags;
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
close(fd);
return err;
}
printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);
return fd;
}
int main()
{
int tun_fd, nread;
char buffer[1500];
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
* IFF_NO_PI - Do not provide packet information
*/
tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);
if (tun_fd < 0) {
perror("Allocating interface");
exit(1);
}
while (1) {
//从tun设备读取数据
nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
printf("Read %d bytes from tun/tap device\n", nread);
}
return 0;
}
编译
# gcc tun.c -o tun
测试运行
执行程序,生成虚拟设备
为tun设备分配IP地址
ping tun设备
尽管在网段192.168.3.XX或192.168.4.XX中除去192.168.3.11和192.168.4.11两个IP地址外,其他不存在(因为我为本地,确定不存在其他IP地址),但是根据路由,ping发出去的报文仍需要通过tun设备进行发送,如下图