基于ubuntu tun虚拟网卡设备完成ping的发送与模拟接收

news2025/1/15 20:38:54

前言

前面我们初步认识了什么是tun设备及基础的工作原理与接收文件的设备文件(节点)、虚拟网卡的启动、添加路由表等操作,为什么进一步理解tun设备与协议栈的通信理解,这次我们将应用层控制tun设备发送ping,通过read读取到,后经过手动组装icmp网络包write到协议栈,模拟远程主机回复给我们的 icmp reply,顺便熟悉下icmp 网络包的协议。

模拟ping发送与接收有什么作用

网络测试和调试:TUN设备可以用于模拟网络环境,以测试和调试网络应用程序和协议。通过模拟Ping发送和接收,您可以检查网络连接是否正常工作,诊断延迟问题,以及测试网络拓扑。

完成事项

在ubuntu 设中使用ping指定网卡命令使得tun设备发送ping包,tun read到后,定义组装icmp reply、调用系统调用write到协议栈,通过tcpdump抓包,验证是否模拟成功

作用

  1. 提高tun 设备的发送、接收与read与write的理解。
  2. 了解ping的协议,ip header、icmp header、icmp date的定义,赋值,校验和等,协议字段的不同,导致的无法被识别为目标request 的reply,为的ping外网做技术储备。

操作列表

  1. 用于读取tun设备节点并处理数据的应用层程序,再模拟发送icmp reply包
  2. 通过ping命令使得tun设备发送icmp包
  3. tcpdump工具抓取流经tun网卡的包

验证与代码流程

  • 用于读取tun设备节点并处理数据的应用层程序,再模拟发送icmp reply包
#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>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip_icmp.h>


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");
    printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);

    return fd;
}
			int tun_fd, nread;
            char buffer[1500];
            char buffer7[1000];
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");
uint16_t calculate_checksum(uint16_t *data, int length) {
    uint32_t sum = 0;
    while (length > 1) 
    {
        sum += *data++;
        length -= 2;
    }
    if (length > 0)
    {
        sum += *((uint8_t *)data);
    }
    while (sum >> 16) 
    {
        sum = (sum & 0xFFFF) + (sum >> 16);
    }
    return (uint16_t)(~sum);
}

                memset(buffer7,0,sizeof(buffer7));
                    // 检查数据包是否为 ICMP Echo 请求
                struct iphdr *ip_header = (struct iphdr *)buffer7;
                if( buffer[20]==0x08)
    {
                struct icmphdr *icmp_header = (struct icmphdr *)(buffer7 + sizeof(struct iphdr));
                printf("--------是icmp request包-----------\n");
                printf("--------ID:%02X,%02X,seq num:%02X,%02X-----------\n",buffer[24],buffer[25],buffer[26],buffer[27]);
                unsigned char id = buffer[24];
                unsigned char se = buffer[26];
                unsigned char id1 = buffer[25];
                unsigned char se1 = buffer[27];
                unsigned short ID = ((unsigned short)id << 8 )|id1;
                unsigned short seq = ((unsigned short)se << 8 )|se1;
                printf("----------ID:%04X,seq:%04X--------------\n",ID,seq);

                // Manually construct IP header
                ip_header->version = 4;
                ip_header->ihl = 5; // Header length in 32-bit words (5 for no options)
                ip_header->tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) + 56);
                ip_header->id = htons(12345); // Customize the ID if needed
                ip_header->ttl = 64; // Time to Live
                ip_header->protocol = IPPROTO_ICMP;
                ip_header->saddr = inet_addr("192.168.100.137"); // Source IP address
                ip_header->daddr = inet_addr("10.0.0.1"); // Destination IP address
                ip_header->check = 0; // Checksum (set to 0 for now, will be calculated later)
                
                // Create ICMP header for Echo Reply
                icmp_header->type = ICMP_ECHOREPLY;
                icmp_header->code = 0;
                icmp_header->checksum = 0;
                icmp_header->un.echo.id = htons(ID); // Customize the ID if needed
                icmp_header->un.echo.sequence = htons(seq);// Customize the sequence number if needed

                char *data = buffer7 + sizeof(struct iphdr) + sizeof(struct icmphdr);
                memcpy(data,&(buffer[28]),8);
                memcpy(data+8,&(buffer[36]),48);
                icmp_header->checksum = calculate_checksum((uint16_t *)icmp_header, sizeof(struct icmphdr) + 56);
                printf("------------sizeof(struct icmphdr) + sizeof(reply_data):%d--------------------\n",(sizeof(struct icmphdr) + 56));

                // 发送 ICMP Echo Reply 数据包到 TUN 设备
                ssize_t bytes_written=write(tun_fd, buffer7, (sizeof(struct icmphdr) + 56+sizeof(struct iphdr)));
                if (bytes_written < 0) {
                perror("Failed to write to TUN device");
                break;
                }
                printf("---------ICMP Echo Request sent :%d Bytes-------------\n",bytes_written);
        }
}
            close(tun_fd);
            return 0;
}

编译并执行结果
编译:

$ gcc net_device_user1.c -o net_device_user1
net_device_user1.c: In function ‘main’:
net_device_user1.c:143:28: warning: format ‘%zd’ expects argument of type ‘signed size_t’, but argument 2 has type ‘int’ [-Wformat=]
             printf("Read %zd bytes from tun/tap device\n", nread);
                          ~~^
                          %d
net_device_user1.c:401:82: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
                 printf("------------sizeof(struct icmphdr) + sizeof(reply_data):%d--------------------\n",(sizeof(struct icmphdr) + 56));
                                                                                 ~^
                                                                                 %ld
net_device_user1.c:409:59: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘ssize_t {aka long int}[-Wformat=]
                 printf("---------ICMP Echo Request sent :%d Bytes-------------\n",bytes_written);

执行结果:

$ sudo ./net_device_user1
SIOCADDRT: File exists
Open tun/tap device: tun0 for reading...
  • 通过ping命令使得tun设备发送icmp包
$ ping -I tun0 192.168.100.137
PING 192.168.100.137 (192.168.100.137) from 10.0.0.1 tun0: 56(84) bytes of data.

ping后,应用程序会有新的日志log

$ sudo ./net_device_user1
SIOCADDRT: File exists
Open tun/tap device: tun0 for reading...
Read 84 bytes from tun/tap device
45 00 00 54 2B FFFFFF8B 40 00 40 01 FFFFFFDF FFFFFFEB 0A 00 00 01 
FFFFFFC0 FFFFFFA8 64 FFFFFF89 08 00 21 FFFFFF9D 53 46 00 01 FFFFFFC2 2D FFFFFFF8 64 
00 00 00 00 FFFFFFFC FFFFFFB5 0D 00 00 00 00 00 10 11 12 13 
14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 
24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 
34 35 36 37 
--------是icmp request包-----------
--------ID:53,46,seq num:00,01-----------
----------ID:5346,seq:0001--------------
------------sizeof(struct icmphdr) + sizeof(reply_data):64--------------------
---------ICMP Echo Request sent :84 Bytes-------------
  • tcpdump工具抓取流经tun网卡的包
sudo tcpdump -i tun0 -w tcpdump.pcap
tcpdump: listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
tcpdump: pcap_loop: The interface went down
16 packets captured
18 packets received by filter
0 packets dropped by kernel

打开tcpdump包:
在这里插入图片描述
正常被协议栈识别

PS

处理icmp网络包的应用层有很多细节问题,在代码中也打印了出来,我们可以利用wireshark的二进制来确定协议与打印出来的二进制之间的关系
在这里插入图片描述

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

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

相关文章

【AI绘画--七夕篇】使用Stable Diffusion的艺术二维码完全生成攻略

文章目录 前言Stable Diffusion 简介 什么是云端平台&#xff1f;优势灵活性和可扩展性成本效益高可用性和容错性管理简便性 选择适合的云端平台 平台优势平台操作购买算力并创建工作空间启动工作空间应用市场一键安装 使用Stable-Diffusion作图使用控制网络将文本转图像二维码…

【业务功能篇98】微服务-springcloud-springboot-电商订单模块-生成订单服务-锁定库存

八、生成订单 一个是需要生成订单信息一个是需要生成订单项信息。具体的核心代码为 /*** 创建订单的方法* param vo* return*/private OrderCreateTO createOrder(OrderSubmitVO vo) {OrderCreateTO createTO new OrderCreateTO();// 创建订单OrderEntity orderEntity build…

SpringMVC中的综合案例

目录 一.常用注解 实例&#xff1a; 二.参数转递 2.1. 基础类型 2.2. 复杂类型 2.3. RequestParam 2.4.PathVariable 2.5.RequestBody 2.6.RequestHeader 2.7. 请求方法 三.返回值 3.1.void 3.2.String 3.3 StringModel 3.4 ModelAndView 四、页面跳转 4.1.转发 4.…

zk羊群效应怎么处理

什么是zk的羊群效应 如下图&#xff0c;如果第一个锁挂了&#xff0c;其他客户端又全监听的它&#xff0c;如果几十上百个&#xff0c;之个锁挂了还得去一个个通知&#xff0c;我挂了&#xff0c;你们又要重新加锁&#xff0c;全又监听。会引起多余的请求与网络开销。 如果这个…

虚拟机的ubuntu 22.04无法联网问题解决

问题&#xff1a;虚拟机的ubuntu 22.04无法联网 解决&#xff1a; 找到一种配置的方式&#xff0c;使用命令&#xff1a;sudo dhclient -v

MIT的智慧,利用深度学习来解决了交通堵塞

导读大家都对交通阻塞深恶痛绝。除了让人头疼和错过约会之外&#xff0c;交通拥堵让美国的司机每年多花3000亿美元。 研究人员建议大家使用自动驾驶汽车&#xff0c;即使数量占比并不大&#xff0c;但也能大大改善交通拥堵情况。 Lex Fridman和他的MIT团队开发了一款模拟游戏来…

SpringBoot环境MongoDB分页+去重+获取去重后的原始数据

最近有个比较复杂的MongoDB查询需求&#xff0c; 要求1&#xff1a;获取最近订单表中的请求参数信息&#xff0c;并需要按照请求参数中的账号进行去重 要求2&#xff1a;数据量可能比较大&#xff0c;因此需要做分页查询 研究了大半天&#xff0c;终于搞出了解决方案&#xff0…

使用 Python 的高效相机流

一、说明 让我们谈谈在Python中使用网络摄像头。我有一个简单的任务&#xff0c;从相机读取帧&#xff0c;并在每一帧上运行神经网络。对于一个特定的网络摄像头&#xff0c;我在设置目标 fps 时遇到了问题&#xff08;正如我现在所理解的——因为相机可以用 mjpeg 格式运行 30…

手写Spring:第6章-资源加载器解析文件注册对象

文章目录 一、目标&#xff1a;资源加载器解析文件注册对象二、设计&#xff1a;资源加载器解析文件注册对象三、实现&#xff1a;资源加载器解析文件注册对象3.1 工程结构3.2 资源加载器解析文件注册对象类图3.3 类工具类3.4 资源加载接口定义和实现3.4.1 定义资源加载接口3.4…

面试算法-常用数据结构

文章目录 数据结构数组链表 栈队列双端队列树 1&#xff09;算法和数据结构 2&#xff09;判断候选人的标准 算法能力能够准确辨别一个程序员的功底是否扎实 数据结构 数组 链表 优点&#xff1a; 1&#xff09;O(1)时间删除或者添加 灵活分配内存空间 缺点&#xff1a; 2&…

把文件上传到Gitee的详细步骤

目录 第一步&#xff1a;创建一个空仓库 第二步&#xff1a;找到你想上传的文件所在的地址&#xff0c;打开命令窗口&#xff0c;git init 第三步&#xff1a;git add 想上传的文件 &#xff0c;git commit -m "给这次提交取个名字" 第四步&#xff1a;和咱们在第…

生成多样、真实的评论(2019 IEEE International Conference on Big Data )

论文题目&#xff08;Title&#xff09;&#xff1a;Learning to Generate Diverse and Authentic Reviews via an Encoder-Decoder Model with Transformer and GRU 研究问题&#xff08;Question&#xff09;&#xff1a;评论生成&#xff0c;由上下文评论->生成评论 研…

Android之“写死”数据

何为“写死”&#xff0c;即写完之后除非手动修改&#xff0c;否像嘎了一样在那固定死了 在实际安卓开发中&#xff0c;这种写死的概念必不可少&#xff0c;如控件的id&#xff0c;某一常量&#xff0c;Kotlin中的Val 当然&#xff0c;有些需求可能也会要求我们去写死数据&am…

实战:大数据Flink CDC同步Mysql数据到ElasticSearch

文章目录 前言知识积累CDC简介CDC的种类常见的CDC方案比较 Springboot接入Flink CDC环境准备项目搭建 本地运行集群运行将项目打包将包传入集群启动远程将包部署到flink集群 写在最后 前言 前面的博文我们分享了大数据分布式流处理计算框架Flink和其基础环境的搭建&#xff0c…

入门力扣自学笔记279 C++ (题目编号:1123)

1123. 最深叶节点的最近公共祖先 题目&#xff1a; 给你一个有根节点 root 的二叉树&#xff0c;返回它 最深的叶节点的最近公共祖先 。 回想一下&#xff1a; 叶节点 是二叉树中没有子节点的节点树的根节点的 深度 为 0&#xff0c;如果某一节点的深度为 d&#xff0c;那它…

PyCharm中使用matplotlib.pyplot.show()报错MatplotlibDeprecationWarning的解决方案

其实这只是一个警告&#xff0c;忽略也可。 一、控制台输出 MatplotlibDeprecationWarning: Support for FigureCanvases without a required_interactive_framework attribute was deprecated in Matplotlib 3.6 and will be removed two minor releases later. MatplotlibD…

iOS 17中的Safari配置文件改变了游戏规则,那么如何设置呢

Safari在iOS 17中最大的升级是浏览配置文件——能够在一个应用程序中创建单独的选项卡和书签组。这些也可以跟随你的iPad和Mac&#xff0c;但在本指南中&#xff0c;我们将向你展示如何使用运行iOS 17的iPhone。 你可能有点困惑&#xff0c;为什么Safari中没有明显的位置可以添…

Power BI的发布到web按钮怎么没有?有人知道怎么办吗??????

Power BI的发布到web按钮怎么没有&#xff1f;有人知道怎么办吗&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; .

使用Spring-data-jpa

EnableJpaAuditing 它是用来启动Jpa的审计功能。 jpa querydsl 多表的联合查询 导入依赖 querydsl-jpa 、querydsl-apt Repository接口, 继承QuerydslPredicateExecutor接口 NoRepositoryBean public interface BaseMongoRepository<T> extends MongoRepository<T…

GptFuck—开源Gpt4分享

这个项目不错&#xff0c;分享给大家 项目地址传送门