该demo 创建了一个TUN 设备,添加一条静态路由指定TUN设备,demo 程序从TUN读取报文,简单处理ICMP报文,然后送回协议栈,从而使ping命令成功执行。
运行环境
# uname -a
Linux localhost.localdomain 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
demo 代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
int tun_open(char *dev, int flags)
{
struct ifreq ifr;
int fd, err;
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
{
printf("open /dev/net/tun error %m\n");
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags |= flags;
// 可以指定tun设备名,页可以让系统自动生成
// if (strlen(dev) > 0)
// strncpy(ifr.ifr_name, dev, IFNAMSIZ);
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
close(fd);
printf("set tun error %m\n");
return err;
}
strcpy(dev, ifr.ifr_name);
// 将 tun设备 up
int ctl_fd;
struct ifreq netifr={0};
if ((ctl_fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
{
netifr.ifr_flags |= IFF_UP | IFF_RUNNING;
strncpy(netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
if(ioctl(ctl_fd, SIOCSIFFLAGS, &netifr))
{
printf("Failed to set socket flags:%m\n");
}
else
{
printf("%s up\n",ifr.ifr_name);
}
close(ctl_fd);
}
else
{
printf("open socket error %s", ifr.ifr_name);
}
return fd;
}
int main(int argc, char *argv[])
{
int tun, ret;
char tun_name[IFNAMSIZ];
unsigned char buf[4096];
tun_name[0] = '\0';
// IFF_TUN: 创建一个tun设备
// IFF_TAP: 创建一个tap设备
// IFF_NO_PI: 不包含包信息,默认每个数据包当传到用户空间时,都将包含一个附加的包头来保存包信息
tun = tun_open(tun_name, IFF_TUN | IFF_NO_PI);
if (tun < 0) {
perror("tun_create");
return 1;
}
printf("TUN name is %s\n", tun_name);
char cmd[1024]={0};
// 设置tun 的IP和掩码
snprintf(cmd,sizeof(cmd),"ifconfig %s 192.168.1.201/24",tun_name);
printf("%s\n",cmd);
system(cmd);
// 添加一条静态路由,将10.10.10.0/24 网段报文的出口设置为 tun
snprintf(cmd,sizeof(cmd),"route add -net 10.10.10.0 netmask 255.255.255.0 %s",tun_name);
printf("%s\n",cmd);
system(cmd);
while (1)
{
unsigned char ip[4];
ret = read(tun, buf, sizeof(buf));
if (ret < 0)
{
printf("read tun error %m\n");
break;
}
// 未做报文解析,认为收到的都是ICMP 包,进行简单回复
memcpy(ip, &buf[12], 4);
memcpy(&buf[12], &buf[16], 4);
memcpy(&buf[16], ip, 4);
buf[20] = 0;
*((unsigned short*)&buf[22]) += 8;
printf("read %d bytes\n", ret);
ret = write(tun, buf, ret);
printf("write %d bytes\n", ret);
}
return 0;
}
创tun时flag 必须选择IFF_TUN和IFF_TAP其中的一个,未设置IFF_NO_PI时所附加的包信息头如下:
struct tun_pi {
unsigned short flags;
unsigned short proto;
};
demo 运行前 demo 运行后 网络设备中增加了一个tun0,route 中增加了两条 ping 任意10.10.10.0 网段的IP 都可以ping通 wireshark 抓包可以看到,报文没有以太网的mac地址和类型,直接从IP层开始
参考文章 http://blog.chinaunix.net/uid-317451-id-92474.html
|