Linux的TUN/TAP编程 2

2024-04-30 15:38
文章标签 linux 编程 tap tun

本文主要是介绍Linux的TUN/TAP编程 2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

TUN/TAP虚拟网络设备为用户空间程序提供了网络数据包的发送和接收能力。他既可以当做点对点设备(TUN),也可以当做以太网设备(TAP)。实际上,不仅Linux支持TUN/TAP虚拟网络设备,其他UNIX也是支持的,他们之间只有少许差别。

原理简介

TUN/TAP虚拟网络设备的原理比较简单,他在Linux内核中添加了一个TUN/TAP虚拟网络设备的驱动程序和一个与之相关连的字符设备/dev/net/tun,字符设备tun作为用户空间和内核空间交换数据的接口。当内核将数据包发送到虚拟网络设备时,数据包被保存在设备相关的一个队列中直到用户空间程序通过打开的字符设备tun的描述符读取时,它才会被拷贝到用户空间的缓冲区中,其效果就相当于,数据包直接发送到了用户空间。通过系统调用write发送数据包时其原理与此类似。

值得注意的是:一次read系统调用,有且只有一个数据包被传送到用户空间,并且当用户空间的缓冲区比较小时,数据包将被截断,剩余部分将永久地消失,write系统调用与read类似,每次只发送一个数据包。所以在编写此类程序的时候,请用足够大的缓冲区直接调用系统调用read/write,避免采用C语言的带缓存的IO函数。

准备工作

首先你需要一个能工作的Linux操作系统,并且内核支持TUN/TAP虚拟网络设备,如果没有,请在内核中选中:

Device Drivers => Network device support => Universal TUN/TAP device driver support

你可以选择编译进内核或者是编译成模块,然后重新编译内核并用新内核启动。如果你编译的是模块,那么在下步开始之前,你需要手工加载它。

root@gentux ~ # modprobe tun

开始编程

从代码开始,

12 #include <linux/if_tun.h>
13
14 int tun_create(char *dev, int flags)
15 {
16         struct ifreq ifr;
17         int fd, err;
18
19         assert(dev != NULL);
20
21         if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
22                 return fd;
23
24         memset(&ifr, 0, sizeof(ifr));
25         ifr.ifr_flags |= flags;
26         if (*dev != '\0')
27                 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
28         if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
29                 close(fd);
30                 return err;
31         }
32         strcpy(dev, ifr.ifr_name);
33
34         return fd;
35 }


为了使用TUN/TAP设备,我们必须包含特定的头文件linux/if_tun.h,如12行所示。在21行,我们打开了字符设备/dev/net/tun。接下来我们需要为ioctl的TUNSETIFF命令初始化一个结构体ifr,一般的时候我们只需要关心其中的两个成员ifr_name, ifr_flags。ifr_name定义了要创建或者是打开的虚拟网络设备的名字,如果它为空或者是此网络设备不存在,内核将新建一个虚拟网络设备,并返回新建的虚拟网络设备的名字,同时文件描述符fd也将和此网络设备建立起关联。如果并没有指定网络设备的名字,内核将根据其类型自动选择tunXX和tapXX作为其名字。ifr_flags用来描述网络设备的一些属性,比如说是点对点设备还是以太网设备。详细的选项解释如下:
  • IFF_TUN: 创建一个点对点设备
  • IFF_TAP: 创建一个以太网设备
  • IFF_NO_PI: 不包含包信息,默认的每个数据包当传到用户空间时,都将包含一个附加的包头来保存包信息
  • IFF_ONE_QUEUE: 采用单一队列模式,即当数据包队列满的时候,由虚拟网络设备自已丢弃以后的数据包直到数据包队列再有空闲。
配置的时候,IFF_TUN和IFF_TAP必须择一,其他选项则可任意组合。其中IFF_NO_PI没有开启时所附加的包信息头如下:

struct tun_pi {
    unsigned short flags;
    unsigned short proto;
};


目前,flags只在收取数据包的时候有效,当它的TUN_PKT_STRIP标志被置时,表示当前的用户空间缓冲区太小,以致数据包被截断。proto成员表示发送/接收的数据包的协议。

上面代码中的文件描述符fd除了支持TUN_SETIFF和其他的常规ioctl命令外,还支持以下命令:
  • TUNSETNOCSUM: 不做校验和校验。参数为int型的bool值。
  • TUNSETPERSIST: 把对应网络设备设置成持续模式,默认的虚拟网络设备,当其相关的文件符被关闭时,也将会伴随着与之相关的路由等信息同时消失。如果设置成持续模式,那么它将会被保留供以后使用。参数为int型的bool值。
  • TUNSETOWNER: 设置网络设备的属主。参数类型为uid_t。
  • TUNSETLINK: 设置网络设备的链路类型,此命令只有在虚拟网络设备关闭的情况下有效。参数为int型。
一个小实例

int main(int argc, char *argv[])
{
        int tun, ret;
        char tun_name[IFNAMSIZ];
        unsigned char buf[4096];

        tun_name[0] = '\0';
        tun = tun_create(tun_name, IFF_TUN | IFF_NO_PI);
        if (tun < 0) {
                perror("tun_create");
                return 1;
        }
        printf("TUN name is %s\n", tun_name);

        while (1) {
                unsigned char ip[4];

                ret = read(tun, buf, sizeof(buf));
                if (ret < 0)
                        break;
                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;
}


以上代码简答地处理了ICMP的ECHO包,并回应以ECHO REPLY。

首先运行这个程序:

root@gentux test # ./a.out
TUN name is tun0

接着在另外一个终端运行如下命令:

root@gentux linux-2.6.15-gentoo # ifconfig tun0 0.0.0.0 up
root@gentux linux-2.6.15-gentoo # route add 10.10.10.1 dev tun0
root@gentux linux-2.6.15-gentoo # ping 10.10.10.1
PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data.
64 bytes from 10.10.10.1: icmp_seq=1 ttl=64 time=1.09 ms
64 bytes from 10.10.10.1: icmp_seq=2 ttl=64 time=5.18 ms
64 bytes from 10.10.10.1: icmp_seq=3 ttl=64 time=3.37 ms

--- 10.10.10.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2011ms
rtt min/avg/max/mdev = 1.097/3.218/5.181/1.671 ms

可见,我们顺利地接受到了回应包,这时,切回到前一个终端下:

read 84 bytes
write 84 bytes
read 84 bytes
write 84 bytes
read 84 bytes
write 84 bytes

一切正如我们所预想的那样。

TUN/TAP能做什么?

hoho,问这个问题似乎有些傻,你说一个网卡能做什么?我可以告诉你两个基于此的开源项目:
vtun openvpn ,至于其他的应用,请自由发挥你的想像力吧!

参考资料:

2.6.15内核源码

这篇关于Linux的TUN/TAP编程 2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/949253

相关文章

Linux命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子