Linux tun虚拟网卡通信初识

什么是linux tun设备

Linux TUN 设备是一种虚拟网络设备,用于在用户空间和内核空间之间建立数据通道,使用户空间程序可以通过这个设备与内核网络栈进行交互。TUN 设备是一种通用的网络隧道设备,常用于实现虚拟专用网络(VPN)和其他网络隧道技术。

TUN 设备的工作原理

  1. 将网络数据包从用户空间发送到内核空间,或者从内核空间发送到用户空间。可以收发第三层数据报文包,如IP封包,这使得用户空间的应用程序可以读取和处理传入的数据包,然后将数据包发送回TUN 设备,再由内核负责将数据包发送到目标地址。
  2. 在使用 TUN 设备时,用户空间程序通常会打开 TUN 设备文件,并像读写普通文件一样对其进行读写操作。这样,用户空间程序就可以将网络数据包发送到 TUN 设备或者从 TUN 设备读取接收到的数据包。TUN 设备通常具有一个虚拟的 IP 地址,作为与内核网络栈进行交互的入口和出口

基本处理框架

  1. 创建 TUN 设备: 在 Linux 系统中,可以使用 ip 命令或者其他网络管理工具来创建 TUN 设备。
  2. 用户空间应用程序与 TUN 设备交互: 用户空间的应用程序通常会打开 TUN 设备文件,这类似于打开普通文件。例如,应用程序可以打开 /dev/net/tun 设备文件。
  3. 数据包传输: 当用户空间的应用程序向 TUN 设备文件写入数据包时,数据包将被发送到内核空间的 TUN 设备驱动程序。这个过程是由内核的 TUN/TAP 驱动来完成的。
  4. 内核处理: 内核中的 TUN/TAP 驱动程序接收从用户空间传入的数据包。对于 TUN 设备,它会将数据包解析为 IP 数据包,并将其发送到内核网络栈进行进一步处理。
  5. 数据包处理: 在内核网络栈中,数据包将按照路由表和网络配置进行处理。如果数据包的目标地址与本地网络或者路由表匹配,那么数据包将被内核转发到目标地址。
  6. 接收数据包: 当内核收到其他网络设备传入的数据包(如网络接口收到的数据包),如果目标地址是 TUN 设备的 IP 地址,那么数据包将被传递给 TUN 设备驱动程序。
  7. 用户空间读取: 数据包通过 TUN 设备驱动程序传递到用户空间的应用程序打开的 TUN 设备文件。应用程序可以读取这些数据包并进行处理。

如下是框架流程图:
在这里插入图片描述

linux tun设备可以用作什么技术

  1. VPN(Virtual Private Network): TUN 设备可用于构建 VPN。通过将数据包从用户空间发送到内核空间,再通过TUN 设备进行加密、隧道封装和传输,可以实现安全的远程访问和数据传输。
  2. 隧道技术: TUN 设备也可用于其他隧道技术,如隧道模式下的 IPv6-over-IPv4 和 IPv4-over-IPv6 隧道。它允许不同网络之间通过隧道进行通信。
  3. 加密通信: TUN 设备可以用于实现端到端的加密通信,保护数据的安全性和隐私。
  4. 虚拟专用网络: 使用 TUN 设备,可以创建虚拟专用网络(VPN)或虚拟局域网(VLAN),将不同的网络或子网连接在一起。
  5. 网络隔离: TUN 设备可以用于实现网络隔离,将不同的应用程序或服务隔离在不同的虚拟网络中,增强网络的安全性。
  6. 协议代理: TUN 设备还可以用作协议代理,允许用户空间应用程序处理特定的网络协议,例如将 UDP 或 TCP 流量进行自定义处理。
  7. 网络测试和仿真: 利用 TUN 设备,可以在用户空间中模拟网络环境,用于测试和仿真网络应用程序的性能和稳定性。

本文今天要完成什么?

使用虚拟机ubuntu 自带tun驱动完成:

  1. 虚拟驱动的启动
  2. 应用层发包给虚拟网卡驱动tun并写入设备节点文件,应用层完成读取,加密,写入设备节点并发送给协议栈
  3. 使用tcpdump工具抓取包验证是否正确发送与接收

完成框架

  1. 利用已有的tun设备驱动,打开并配置ip,添加静态路由表
  2. 写两个应用层进程,分别完成数据发送到tun和数据接收处理加密并发回tun驱动

linux已经自带驱动:

ubuntu:/dev/net$ ls 
tun

应用层代码1:
基本逻辑:

  1. 打开虚拟设备网卡
  2. 添加静态路由
  3. 阻塞等待数据的到来
  4. read后,再进行wirite操作
#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>
#include <netinet/in.h>
#include <arpa/inet.h>#define IP_VERSION 4
#define IP_HEADER_LENGTH 20 // IPv4头部长度,单位为字节
#define DEST_IP "10.0.0.2"struct iphdr {unsigned char  ihl_version;unsigned char  tos;unsigned short total_length;unsigned short id;unsigned short frag_off;unsigned char  ttl;unsigned char  protocol;unsigned short checksum;unsigned int   saddr;unsigned int   daddr;
};
int tun_alloc(int flags)
{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 ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {close(fd);return err;}system("sudo ifconfig tun0 10.0.0.1 up");//启动tun虚拟网卡system("sudo route add -net 10.0.0.2 netmask 255.255.255.255 dev tun0");//将所有发送到 10.0.0.2 的数据包,通过网络接口 "tun0" 进行传输,而且这个目标地址被视为一个单独的主机,而不是一个整个网络。system("sudo route add -net 192.168.6.1 netmask 255.255.255.255 dev tun0");printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);return fd;
}int main()
{int tun_fd, nread;char buffer[1500];char buffer1[IP_HEADER_LENGTH + 100]; // IP头部长度 + 应用层数据长度        tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);if (tun_fd < 0) {perror("Allocating interface");exit(1);}
while (1) {//发送数据包到TUN/TAP设备memset(buffer,0,sizeof(buffer));//读取协议栈发送来的信息nread = read(tun_fd, buffer, sizeof(buffer));if (nread < 0) {close(tun_fd);exit(1);}printf("Read %zd bytes from tun/tap device\n", nread);// 以十六进制格式输出IP数据包for (int i = 0; i < nread; i++) {printf("%02X ", buffer[i]);if ((i + 1) % 16 == 0) {printf("\n");}}printf("\n");// 构造 IP 数据包头部,此处就可以进行数据加密,具体的功能未完成,可自行加密处理struct iphdr *ip_header = (struct iphdr *)buffer1;ip_header->ihl_version = (IP_VERSION << 4) | (IP_HEADER_LENGTH / 4);ip_header->tos = 0;ip_header->total_length = htons(IP_HEADER_LENGTH + 8); // IP 头部长度 + ICMP 数据长度ip_header->id = 0;ip_header->frag_off = 0;ip_header->ttl = 64;ip_header->protocol = 1;//IPPROTO_ICMPCMP 协议ip_header->checksum = 0; // 留空,内核会自动计算校验和ip_header->saddr = inet_addr("10.0.0.1"); // 源 IP 地址ip_header->daddr = inet_addr("14.0.0.2"); // 目标 IP 地址// 添加 ICMP 数据char *icmp_data = buffer1 + IP_HEADER_LENGTH;icmp_data[0] = 8; // ICMP 类型为 8(Echo Request)icmp_data[1] = 0; // ICMP 代码为 0icmp_data[2] = 0; // 校验和高位字节icmp_data[3] = 0; // 校验和低位字节icmp_data[4] = 0x12; // 标识符高位字节icmp_data[5] = 0x34; // 标识符低位字节icmp_data[6] = 0; // 序列号高位字节icmp_data[7] = 0; // 序列号低位字节// 计算 ICMP 校验和unsigned short checksum = 0;for (int i = 0; i < 8; i += 2) {checksum += (icmp_data[i] << 8) | icmp_data[i + 1];}checksum = (checksum >> 16) + (checksum & 0xFFFF);checksum = ~checksum;icmp_data[2] = (checksum >> 8) & 0xFF;icmp_data[3] = checksum & 0xFF;// 将数据包写入TUN设备的设备节点ssize_t num_bytes_sent = write(tun_fd, buffer1, IP_HEADER_LENGTH + 8);if (num_bytes_sent < 0) {perror("write");close(tun_fd);return -1;}printf("Sent %zd bytes to TUN device.\n", num_bytes_sent);}close(tun_fd);return 0;
}

应用层2代码
基本逻辑:
负责给虚拟网卡驱动发送应用层数据包

#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>
#include <netinet/in.h>
#include <arpa/inet.h>#define DEST_IP "10.0.0.2"
#define DEST_IP1 "192.168.6.1"int main()
{// 创建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("Error creating socket");//close(tun_fd);exit(1);}sleep(5);// 设置目标 IP 地址和端口printf("------------start-------------------\n");struct sockaddr_in dest_addr;memset(&dest_addr, 0, sizeof(dest_addr));dest_addr.sin_family = AF_INET;dest_addr.sin_port = htons(12345);inet_pton(AF_INET, DEST_IP, &(dest_addr.sin_addr));// 模拟发送数据到 TUN 设备const char* message = "Hello, TUN device!!!!!";int sockfd1 = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd1 < 0) {perror("Error creating socket");//close(tun_fd);exit(1);}sleep(5);// 设置目标 IP 地址和端口printf("------------start-------------------\n");struct sockaddr_in dest_addr1;memset(&dest_addr1, 0, sizeof(dest_addr1));dest_addr1.sin_family = AF_INET;dest_addr1.sin_port = htons(1234);inet_pton(AF_INET, DEST_IP1, &(dest_addr1.sin_addr));// 模拟发送数据到 TUN 设备const char* message1 = "tun tunt tunt!!!!!!";while(1){sendto(sockfd1, message1, strlen(message1), 0, (struct sockaddr*)&dest_addr1, sizeof(dest_addr1));        sleep(5);sendto(sockfd, message, strlen(message), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));}close(sockfd);close(sockfd1);}

应用层如何编译

如果您经常阅读我的文章,就不应该问出这样的问题,以前的文章都有提及!

验证

root权限执行应用层代码1结果(有删减):

sudo ./net_device_user1
RTNETLINK answers: File exists
Open tun/tap device: tun0 for reading...Read 50 bytes from tun/tap device
45 00 00 32 03 FFFFFF9E 40 00 40 11 23 1B 0A 00 00 01 
0A 00 00 02 FFFFFF86 0D 30 39 00 1E 05 27 48 65 6C 6C 
6F 2C 20 54 55 4E 20 64 65 76 69 63 65 21 21 21 
21 21 
Sent 28 bytes to TUN device.
Read 47 bytes from tun/tap device
45 00 00 2F 03 FFFFFF9F 40 00 40 11 23 1D 0A 00 00 01 
0A 00 00 02 FFFFFFC0 3E 04 FFFFFFD2 00 1B FFFFFFF3 FFFFFFDE 74 75 6E 20 
74 75 6E 74 20 74 75 6E 74 21 21 21 21 21 21 
Sent 28 bytes to TUN device.

root权限执行应用层代码2结果

sudo ./net_device.o
------------start-------------------

抓取tupdump包

sudo tcpdump -i tun0 -w tcpdump_30.pcap
tcpdump: listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
tcpdump: pcap_loop: The interface went down
16 packets captured
16 packets received by filter
0 packets dropped by kernel

在这里插入图片描述
分别抓到了应用层发来的数据包(两个包),读取完后完成数据包的发送

利用十六进制转字符串验证应用代码2发送给应用代码1的数据是否发送成功:

第一个包

Read 50 bytes from tun/tap device
45 00 00 32 03 FFFFFF9E 40 00 40 11 23 1B 0A 00 00 01 
0A 00 00 02 FFFFFF86 0D 30 39 00 1E 05 27 48 65 6C 6C 
6F 2C 20 54 55 4E 20 64 65 76 69 63 65 21 21 21 
21 21

其中的data数据转化结果:
在这里插入图片描述
第二个包

Read 47 bytes from tun/tap device
45 00 00 2F 03 FFFFFF9F 40 00 40 11 23 1D 0A 00 00 01 
0A 00 00 02 FFFFFFC0 3E 04 FFFFFFD2 00 1B FFFFFFF3 FFFFFFDE 74 75 6E 20 
74 75 6E 74 20 74 75 6E 74 21 21 21 21 21 21 

在这里插入图片描述

结果

验证成功

ps

完成的功能很少,希望这能抛砖引玉
代码解释不是特别详细,代码行中有很多注释,希望能帮助到你

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

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

相关文章

【湍流介质的三维传播模拟器】全衍射3-D传播模拟器,用于在具有随机和背景结构的介质中传播无线电和光传播(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

华为OD机试(含B卷)真题2023 算法分类版,58道20个算法分类,如果距离机考时间不多了,就看这个吧,稳稳的

目录 一、数据结构1、线性表2、优先队列3、滑动窗口4、二叉树5、并查集6、栈 二、算法1、基础算法2、字符串3、图4、动态规划5、数学 三、漫画算法2&#xff1a;小灰的算法进阶参与方式 很多小伙伴问我&#xff0c;华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如…

中文版开源Llama 2同时有了语言、多模态大模型,完全可商用

可以说&#xff0c;AI 初创公司 LinkSoul.Al 的这些开源项目让海外开源大模型在国内的普及和推广速度与国际几乎保持了一致。 7 月 19 日&#xff0c;Meta 终于发布了免费可商用版本 Llama 2&#xff0c;让开源大模型领域的格局发生了巨大变化。 Llama 2 模型系列包含 70 亿、…

Python识别抖音Tiktok、巨量引擎滑块验证码识别

由于最近比较忙&#xff0c;所以本周搞了一个相对简单的验证码&#xff0c;就是抖音Tiktok的滑块验证码&#xff0c;这也是接到客户的一个需求。这种验证码通常在电脑端登录抖音、巨量引擎的的时候出现。 首先看一下最终的效果&#xff1a; 验证码识别过程 1、利用爬虫采集图…

查看单元测试用例覆盖率新姿势:IDEA 集成 JaCoCo

1、什么是 IDEA IDEA 全称 IntelliJ IDEA&#xff0c;是 Java 编程语言开发的集成环境。IntelliJ 在业界被公认为最好的 Java 开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE 支持、各类版本工具(git、SVN 等)、JUnit、CVS 整合、代码分析、 创新的 GUI…

04-5_Qt 5.9 C++开发指南_QComboBox和QPlainTextEdit

文章目录 1. 实例功能概述2. 源码2.1 可视化UI设计2.2 widget.h2.3 widget.cpp 1. 实例功能概述 QComboBox 是下拉列表框组件类&#xff0c;它提供一个下拉列表供用户选择&#xff0c;也可以直接当作一个QLineEdit 用作输入。OComboBox 除了显示可见下拉列表外&#xff0c;每个…

【Python学习】Python大版本新增内容精选

&#x1f308;据说&#xff0c;看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。 前言&#xff1a; &#x1f9e1;作者简介&#xff1a;大家好我是 user_from_future &#xff0c;意思是 “ 来自未来的用户 ” &#xff0c;寓意着未来的自己一定很棒~ ✨个…

flutter:Future、Stream、RxDart

Future 在Flutter中&#xff0c;Future是Dart语言中的一个类&#xff0c;用于表示异步操作的结果。与Future相关的的重要关键字包括async和await。 async&#xff1a;这个关键字用于在方法或函数声明前添加&#xff0c;以指示该方法为异步方法。在异步方法中&#xff0c;执行…

IPv6地址分类,EUI-64转换规则

1、可聚合的单全球单播地址Global Unique Address&#xff1a; Aggregate global unicast address&#xff0c;前3位是001&#xff0c;即2000::/3&#xff0c;目前IANA已经将一部分可聚合全球单播进行了专门使用&#xff0c;如&#xff1a;2001::/16用于IPV6互联网&#xff0c;…

考研数据结构上机题【36个模块77道题】5万字帮助你学会考研算法【完结篇】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

代码随想录—力扣算法题:209长度最小的子数组.Java版(示例代码与导图详解)

版本说明 当前版本号[20230808]。 版本修改说明20230808初版 目录 文章目录 版本说明目录209.长度最小的子数组思路暴力解法滑动窗口 两种方法的区别总结 209.长度最小的子数组 力扣题目链接 更多内容可点击此处跳转到代码随想录&#xff0c;看原版文件 给定一个含有 n 个…

基于gpt4all的企业内部知识问答服务应用搭建

文章目录 痛点项目缘起技术选型fine-tuningfew shot prompt engineering选定方案的特征描述 模型赛马gpt4all调优部署时踩坑python3.9 header缺失 -- 安装下缺失的就行运行时参数调优 代码分析项目代码库代码 效果展示例子1例子2 附录&#xff1a;所用的公司内部API文档例子&am…

安全学习DAY14_JS信息打点

信息打点——前端JS框架 文章目录 信息打点——前端JS框架小节概述-思维导图JS安全概述什么是JS渗透测试&#xff1f;前后端差异JS安全问题流行的Js框架如何判定JS开发应用&#xff1f; 测试方法&#xff08;JS文件的获取以及分析方法1、手工搜索分析2、半自动Burp分析插件介绍…

万字长文解析深度学习中的术语

引言 新手在学习深度学习或者在看深度学习论文的过程中&#xff0c;有不少专业词汇&#xff0c;软件翻译不出来&#xff0c;就算是翻译出来也看不懂&#xff0c;因为不少术语是借用其他学科的概念&#xff0c;这里整理了一些在深度学习中常见的术语&#xff0c;并对一些概念进…

Axure RP9中使用Echarts示例

目录 在Axure中拖入一个矩形框&#xff0c;并命名tes 进入Echarts官网示例页面https://echarts.apache.org/examples/zh/index.html 选择自己需要的图表&#xff0c;修改数据&#xff0c;并复制左侧js代码 把上面复制的代码替换下方的option{}; javascript: var script docum…

无涯教程-Perl - References(引用)

Perl引用是一个标量数据类型&#xff0c;该数据类型保存另一个值的位置&#xff0c;该值可以是标量&#xff0c;数组或哈希。 创建引用 变量&#xff0c;子程序或值创建引用很容易&#xff0c;方法是在其前面加上反斜杠&#xff0c;如下所示: $scalarref \$foo; $arrayref …

【Spring】使用注解存储Bean对象

目录 一、配置扫描路径&#xff08;使用注解的方式存对象的前提&#xff09; 二、使用类注解存储Bean对象 1、使用五大类注解存储Bean对象 2、为什么要这么多的类注解&#xff1f; 2.1、五大类注解之间的关系 3、获取Bean对象时的默认命名规则 三、使用方法注解来存储…

动画制作选择Blender还是Maya

Blender和Maya是两种最广泛使用的 3D 建模和动画应用程序。许多经验丰富的用户表示&#xff0c;Blender 在雕刻工具方面远远领先于 Maya&#xff0c;并且在 3D 建模方面达到了相同的质量水平。对于刚接触动画行业的人来说&#xff0c;您可能会问“我应该使用 Blender 还是 Maya…

使用Openoffice或LibreOffice实现World、Excel、PPTX在线预览

使用Openoffice或LibreOffice实现World、Excel、PPTX在线预览 预览方案使用第三方服务使用前端库转换格式 jodconverterjodconverter概述主要特性OpenOfficeLibreOffice jodconverter的基本使用添加依赖配置创建DocumentConverter实例上传与转换预览启动上传与预览World 与Spri…

设计模式行为型——状态模式

在软件开发过程中&#xff0c;应用程序中的部分对象可能会根据不同的情况做出不同的行为&#xff0c;把这种对象称为有状态的对象&#xff0c;而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时&#xff0c;其内部状态就会发生改变&…