利用 Linux tap/tun 虚拟设备写一个 ICMP echo 程序

利用 Linux tap/tun 虚拟设备写一个 ICMP echo 程序

前面两篇文章已经介绍过 tap/tun 的原理和配置工具。这篇文章通过一个编程示例来深入了解 tap/tun 的程序结构。

01 准备工作

首先通过 modinfo tun 查看系统内核是否支持 tap/tun 设备驱动。

 
Copy
[root@by ~]# modinfo tun filename: /lib/modules/3.10.0-862.14.4.el7.x86_64/kernel/drivers/net/tun.ko.xz alias: devname:net/tun alias: char-major-10-200 license: GPL author: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com> description: Universal TUN/TAP device driver retpoline: Y rhelversion: 7.5 srcversion: 50878D5D5A0138445B25AA8 depends: intree: Y vermagic: 3.10.0-862.14.4.el7.x86_64 SMP mod_unload modversions signer: CentOS Linux kernel signing key sig_key: E4:A1:B6:8F:46:8A:CA:5C:22:84:50:53:18:FD:9D:AD:72:4B:13:03 sig_hashalgo: sha256

在 linux 2.4 及之后的内核版本中,tun/tap 驱动是默认编译进内核中的。

如果你的系统不支持,请先选择手动编译内核或者升级内核。编译时开启下面的选项即可:

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

tap/tun 也支持编译成模块,如果编译成模块,需要手动加载它:

 
Copy
[root@localhost ~]# modprobe tun [root@localhost ~]# lsmod | grep tun tun 31665 0

关于以上的详细步骤,网上有很多教程,这里就不再赘述了。

https://blog.csdn.net/lishuhuakai/article/details/70305543

上面只是加载了 tap/tun 模块,要完成 tap/tun 的编码,还需要有设备文件,运行命令:

 
Copy
mknod /dev/net/tun c 10 200 # c表示为字符设备,10和200分别是主设备号和次设备号

这样在 /dev/net 下就创建了一个名为 tun 的文件。

02 编程示例

2.1 启动设备

使用 tap/tun 设备,需要先进行一些初始化工作,如下代码所示:

 
Copy
int tun_alloc(char *dev, int flags) { assert(dev != NULL); struct ifreq ifr; int fd, err; char *clonedev = "/dev/net/tun"; if ((fd = open(clonedev, O_RDWR)) < 0) { return fd; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = flags; if (*dev != '\0') { strncpy(ifr.ifr_name, dev, IFNAMSIZ); } if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) { close(fd); return err; } // 一旦设备开启成功,系统会给设备分配一个名称,对于tun设备,一般为tunX,X为从0开始的编号; // 对于tap设备,一般为tapX strcpy(dev, ifr.ifr_name); return fd; }

首先打开字符设备文件 /dev/net/tun,然后用 ioctl 注册设备的工作模式,是 tap 还是 tun。这个模式由结构体 struct ifreq 的属性 ifr_flags 来定义,它有以下表示:

  • IFF_TUN: 表示创建一个 tun 设备。
  • IFF_TAP: 表示创建一个 tap 设备。
  • IFF_NO_PI: 表示不包含包头信息,默认的,每个数据包传到用户空间时,都会包含一个附加的包头来保存包信息,这个表示不加包头。
  • IFF_ONE_QUEUE:表示采用单一队列模式。

还是有一个属性是 ifr_name,表示设备的名字,它可以由用户自己指定,也可以由系统自动分配,比如 tapXtunX,X 从 0 开始编号。

ioctl 完了之后,文件描述符 fd 就和设备建立起了关联,之后就可以根据 fd 进行 read 和 write 操作了。

2.2 写一个 ICMP 的调用函数

为了测试上面的程序,我们写一个简单的 ICMP echo 程序。我们会使用 tun 设备,然后给 tunX 接口发送一个 ping 包,程序简单响应这个包,完成 ICMP 的 request 和 reply 的功能。

如下代码所示:

 
Copy
int main() { int tun_fd, nread; char buffer[4096]; char tun_name[IFNAMSIZ]; tun_name[0] = '\0'; /* Flags: IFF_TUN - TUN device (no Ethernet headers) * IFF_TAP - TAP device * IFF_NO_PI - Do not provide packet information */ tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); if (tun_fd < 0) { perror("Allocating interface"); exit(1); } printf("Open tun/tap device: %s for reading...\n", tun_name); while (1) { unsigned char ip[4]; // 收包 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); // 简单对收到的包调换一下顺序 memcpy(ip, &buffer[12], 4); memcpy(&buffer[12], &buffer[16], 4); memcpy(&buffer[16], ip, 4); buffer[20] = 0; *((unsigned short *)&buffer[22]) += 8; // 发包 nread = write(tun_fd, buffer, nread); printf("Write %d bytes to tun/tap device, that's %s\n", nread, buffer); } return 0; }

下面测试一下。

2.3 给 tap/tun 设备配置 IP 地址

编译:

 
Copy
[root@localhost coding]# gcc -o taptun taptun.c [root@localhost coding]# ./taptun Open tun/tap device: tun0 for reading...

开另一个终端,查看生成了 tun0 接口:

 
Copy
[root@localhost coding]# ip a 6: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 500 link/none

给 tun0 接口配置 IP 并启用,比如 10.1.1.2/24

 
Copy
[root@localhost ~]# ip a a 10.1.1.2/24 dev tun0 [root@localhost ~]# ip l s tun0 up

再开一个终端,用 tcpdump 抓 tun0 的包。

 
Copy
[root@localhost ~]# tcpdump -nnt -i tun0

然后在第二个终端 ping 一下 10.1.1.0/24 网段的 IP,比如 10.1.1.3,看到:

 
Copy
[root@localhost ~]# ping -c 4 10.1.1.3 PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data. 64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.133 ms 64 bytes from 10.1.1.3: icmp_seq=2 ttl=64 time=0.188 ms 64 bytes from 10.1.1.3: icmp_seq=3 ttl=64 time=0.092 ms 64 bytes from 10.1.1.3: icmp_seq=4 ttl=64 time=0.110 ms --- 10.1.1.3 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3290ms rtt min/avg/max/mdev = 0.092/0.130/0.188/0.038 ms

由于 tun0 接口建好之后,会生成一条到本网段 10.1.1.0/24 的默认路由,根据默认路由,数据包会走 tun0 口,所以能 ping 通,可以用 route -n 查看。

再看 tcpdump 抓包终端,成功显示 ICMP 的 request 包和 reply 包。

 
Copy
[root@localhost ~]# tcpdump -nnt -i tun0 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 3250, seq 1, length 64 IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 3250, seq 1, length 64 IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 3250, seq 2, length 64 IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 3250, seq 2, length 64

再看程序 taptun.c 的输出:

 
Copy
[root@localhost coding]# ./taptun Open tun/tap device: tun0 for reading... Read 48 bytes from tun/tap device Write 48 bytes to tun/tap device Read 48 bytes from tun/tap device Write 48 bytes to tun/tap device

ok,以上便验证了程序的正确性。

03 总结

通过这个小例子,让我们知道了基于 tap/tun 编程的流程,对 tap/tun 又加深了一层理解。

使用 tap/tun 设备需要包含头文件 #include <linux/if_tun.h>,以下是完整代码。

 
Copy
/****************************************************************************** * File Name: taptun.c * Author: 公众号: CloudDeveloper * Created Time: 2019年02月23日 星期六 21时28分24秒 *****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <assert.h> #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> int tun_alloc(char *dev, int flags) { assert(dev != NULL); struct ifreq ifr; int fd, err; char *clonedev = "/dev/net/tun"; if ((fd = open(clonedev, O_RDWR)) < 0) { return fd; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = flags; if (*dev != '\0') { strncpy(ifr.ifr_name, dev, IFNAMSIZ); } if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) { close(fd); return err; } // 一旦设备开启成功,系统会给设备分配一个名称,对于tun设备,一般为tunX,X为从0开始的编号; // 对于tap设备,一般为tapX strcpy(dev, ifr.ifr_name); return fd; } int main() { int tun_fd, nread; char buffer[4096]; char tun_name[IFNAMSIZ]; tun_name[0] = '\0'; /* Flags: IFF_TUN - TUN device (no Ethernet headers) * IFF_TAP - TAP device * IFF_NO_PI - Do not provide packet information */ tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); if (tun_fd < 0) { perror("Allocating interface"); exit(1); } printf("Open tun/tap device: %s for reading...\n", tun_name); while (1) { unsigned char ip[4]; // 收包 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); // 简单对收到的包调换一下顺序 memcpy(ip, &buffer[12], 4); memcpy(&buffer[12], &buffer[16], 4); memcpy(&buffer[16], ip, 4); buffer[20] = 0; *((unsigned short *)&buffer[22]) += 8; // 发包 nread = write(tun_fd, buffer, nread); printf("Write %d bytes to tun/tap device, that's %s\n", nread, buffer); } return 0; } 原文地址https://www.cnblogs.com/bakari/p/10474600.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/449254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2020-3-21

题目一&#xff1a; JavaScript 获取月份最后一天日期 月份最后一天日期可能是不同的&#xff0c;比如有的是30、有的是31还有的是28。 <!DOCTYPE html><html> <head> <meta charset" utf-8"> <script type"text/javascript"&…

正方形矩阵求对角线之和

nint(input()) a[] for i in range(n): #循环体里面加入input&#xff08;&#xff09;可以实现一共执行n次input&#xff08;&#xff09; lst[int(x) for x in input().split()]a.append(lst) #用列表解析&#xff0c;两层列表代表行列&#xff0c;很巧妙的方法 w0 bl…

解决: Unable to connect to zookeeper server within timeout: 5000

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一个项目启动不起来了&#xff0c;报错如题&#xff1a; Caused by: org.I0Itec.zkclient.exception.ZkTimeoutException: Unable to c…

闲钱请看如何处理

买一点基金定投。基金是专家帮你理财。基金的起始资金最低单笔是1000元,定投200元起投 买基金到银行或者基金公司都行。银行能代理很多基金公司的业务&#xff0c;具体开户找银行理财专柜办理。现在有些证券公司也有代理基金买卖的。在银行开通网上银行后网上购买一般收费上有优…

JAVA 数组元素的反转

package Code411;/*数组元素的反转本来[1,2,3,4]反转后[4,3,2,1]1.对称位置的元素交换2.对称位子需要两个索引3.int temp a&#xff1b;ab;btemp;4.什么时候停止交换&#xff08;1&#xff09;minmax (2)min>max */public class CodeArrayReverse { public static void m…

requests模块相关用法

requests模块 -1. 什么是requests模块- python原生的一个基于网络请求的模块&#xff0c;模拟浏览器发起请求。 -2. 为什么使用requests模块-1. 自动处理url编码-2. 自动处理post请求参数-3. 简化cookie和代理的操作-3. requests模块如何被使用安装&#xff1a; pip install re…

2020-3-22

题目一&#xff1a; JavaScript 天小时分钟和秒倒计时 代码与解析&#xff1a; <!DOCTYPE html> <html> <head> <meta charset" utf-8"> <style type"text/css"> *{margin:0;padding:0;list-style:none; } body{font-size:…

TeamViewer13 -- 安装、使用说明

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Teamviewer13是一款强大的远程桌面控制工具&#xff0c;同时也是一款非常实用的共享文件传输工具&#xff0c;它拥有简洁的界面以及方便快…

论程序员的自我修炼

摘要&#xff1a;软件开发是个挑战&#xff0c;尤其是对于那些完美主义的程序员。为了想要成为更好的程序员&#xff0c;总会开发出一些特别的功能&#xff0c;在软件设计上想要做到零瑕疵&#xff0c;但事实上&#xff0c;完美的软件设计是根本不存在的&#xff0c;试着让你的…

利用 Charles Proxy 下载旧版本 iOS App

一、软件准备 1、旧版本 iTunes1.IPSW Downloads&#xff1a;https://ipsw.me/2.百度网盘链接&#xff1a;https://pan.baidu.com/s/1PO9Z12o-rqZ_JG68zRqEnA 提取码&#xff1a;fe1v 2、抓包工具 Charles Proxy1.Charles官网链接&#xff1a;https://www.charlesproxy.com/2.百…

禅道8.2-9.2.1注入GetShell

漏洞分析附上某老哥的漏洞分析&#xff0c;来了解下原理。 漏洞利用查看版本&#xff1a;访问Url&#xff1a;http://127.0.0.1/zentao/index.php?modegetconfig即可获取禅道的版本号以及一些其他的信息&#xff0c;目前漏洞存在于v8.2~v9.2确定版本号之后&#xff0c;我们就…

2020-3-23

题目一&#xff1a; JavaScript 复选框全选和全不选 <!DOCTYPE html> <html> <head> <meta charset" utf-8"> <style type"text/css"> body{font-size:12px;} ul{list-style:none} </style> <script type"te…

Maven 依赖-镜像仓库替换为 -- 阿里云镜像仓库(飞快实现 pom 引入)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 在本地 maven 的 setting 配置文件中加上阿里云镜像地址就行了&#xff1a; 新增配置内容&#xff1a; <!-- 阿里镜像仓库 --><…

cmake 常用命令

1. 使用日期 # 获取时间 string(TIMESTAMP DATE_TIME "%y-%m-%d %H:%M")# 获取日期 string(TIMESTAMP DATE_VERSION "%m%d") 转载于:https://www.cnblogs.com/ziyu-trip/p/10697309.html

爱屋吉屋病死后,链家、中原、我爱我家们却哭不得笑不得

作为互联网房产中介最快成长为行业独角兽的爱屋吉屋&#xff0c;还是没能迎来开春之暖&#xff0c;于2019年2月19日便停止了运营。对于这个享有创造中国房市273天四轮融资的奇迹、估值超过10亿美元的速成独角兽、一代“革命者”、创造世界纪录的互联网房产神话等多个美誉企业的…

2020-3-24

题目一&#xff1a; JavaScript 页面跳转效果 实现在10秒之后跳转到指定页面。 代码与解析&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <script type"text/javascript"> function countDown(secs,…

清洗肾脏的绝好秘方(图)

清洗肾脏的绝好秘方&#xff1a;只用香菜&#xff0c;可以在家里尝试。不用花多少钱就可清洗你的肾脏。经过多年来&#xff0c;我们的肾脏过滤血液&#xff0c;排除盐&#xff0c;毒素及所有不需要的物质进入我们的身体。随着时日&#xff0c;盐毒积累&#xff0c;这需要进行清…

列表 字典

一、列表 1.定义&#xff1a; stus ["s1","s2","s3","s4","s5","s6","s7"] #List 列表 数组用中括号定义&#xff0c;逗号隔开 stus2 [] #空的列表stus3 list() #空的列表 【定义空的列表便于往里…

MySQL执行外部sql脚本文件的命令( source命令执行sql )

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 sql脚本是包含一到多个sql命令的sql语句&#xff0c;我们可以将这些sql脚本放在一个文本文件中&#xff08;我们称之为“sql脚本文件”&…

【案例】数据量猛增,BI分析效率太低怎么破?

2019独角兽企业重金招聘Python工程师标准>>> 近日&#xff0c;Apache Kylin Innovation Meetup 在上海成功举办&#xff0c;有近200位小伙伴来到了现场。此次会议特别邀请到了金融、互联网等行业的技术伙伴分享了 Kylin 在行业中的实操应用 。今天将首先与大家分享演…