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


    前言

    前面我们初步认识了什么是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 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    编译并执行结果
    编译:

    $ 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行结果:

    $ sudo ./net_device_user1
    SIOCADDRT: File exists
    Open tun/tap device: tun0 for reading...
    
    • 1
    • 2
    • 3
    • 通过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.
    
    • 1
    • 2

    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-------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    PS

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

  • 相关阅读:
    Unity UGUI的CanvasScaler(画布缩放器)组件的介绍及使用
    uniapp微信小程序的代码加固
    Spring高手之路11——BeanDefinition解密:构建和管理Spring Beans的基石
    UML 类图几种关系(依赖、关联、泛化、实现、聚合、组合)及其对应代码
    Lesson1:酶预测大赛1
    零基础学习C语言,怎么入门?
    提高使用VS Code工作效率的技巧
    groovy笔记
    【C++基础】8. 函数
    3D全景虚拟旅游在旅游行业中具备哪些应用价值?
  • 原文地址:https://blog.csdn.net/weixin_42271802/article/details/132717563