该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#include #include #include #include #include #include #include #include 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 %mn"); 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 %mn"); 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:%mn"); } else { printf("%s upn",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] = ' '; // 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 %sn", tun_name); char cmd[1024]={0}; // 设置tun 的IP和掩码 snprintf(cmd,sizeof(cmd),"ifconfig %s 192.168.1.201/24",tun_name); printf("%sn",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("%sn",cmd); system(cmd); while (1) { unsigned char ip[4]; ret = read(tun, buf, sizeof(buf)); if (ret < 0) { printf("read tun error %mn"); 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 bytesn", ret); ret = write(tun, buf, ret); printf("write %d bytesn", 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



