【Linux5.4】【TUN】简单用例--模拟ICMP_ECHOREPLY包(ping反馈)

TUN模块用例–模拟ICMP_ECHOREPLY包(ping反馈)

使用TUN模块模拟ICMP_ECHOREPLY包,也就是ping命令的回包功能。
由于TUN设备虚拟三层网络设备,因此在read()读取TUN数据时获得的时带有IP头部的数据,write()写入回包的数据要从新计算IP校验码和ICMP校验码。

IP头部校验

//IP头部校验
uint16_t ip_checksum(uint16_t *buf, int size)
{
    uint32_t sum = 0;
    while(size >1)
    {
        sum += *(buf++);
        size -= 2;
    }
    while(sum >> 16)
    {
        sum = (sum >> 16) + (sum & 0xffff);
    }
    return ~sum;
}

ICMP头部校验码

//ICMP头部校验
uint16_t icmp_checksum(unsigned char *buffer, int len)
{
    uint32_t checksum = 0;
    uint16_t *data = (u_int16_t *)buffer;

    while(len > 1)
    {
        checksum += *data++;
        len -= 2;
    }
    if(1 == len)
    {
        u_int16_t tmp = *data;
        tmp &= 0xff00;
        checksum += tmp;
    }
    checksum = (checksum >> 16) + (checksum & 0x0000ffff);
    checksum += checksum >> 16;
    checksum = ~checksum;
    return checksum;
}

TUN设备创建并设置属性

int initTun(char *aDev, uint32_t aLocalAddr, uint32_t aNetMask)
{
    int tunFd;
    struct ifreq ifr;
    struct sockaddr_in si;
    int err = 0, sockSetip = 0;
    /* 打开tun设备 */
    if ((tunFd = open("/dev/net/tun", O_RDWR)) < 0)
    {
        printf("Open tun file error!\n");
        return tunFd;
    }
    /* 设置tun设备名称,默认设备名格式为'tun+数字' */
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI ;//| IFF_NO_PI 告诉内核不需要提供报文信息,即不需要其它字节,否则会提供额外四字节(2字节标识,2字节协议)
    strncpy(ifr.ifr_ifrn.ifrn_name, aDev, IFNAMSIZ);
    if ((err = ioctl(tunFd, TUNSETIFF, &ifr)) < 0)
    {
        printf("ioctl TUNSETIFF ERR\n");
        close(tunFd);
        return err;
    }
    /* 设置虚拟网卡属性,需要借用socket+ioctl,打开一个socket */
    sockSetip = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
    if (sockSetip < 0)
    {
        printf("Failed to open sockSetip\n");
    }
    /* 获取socket中的ifr属性 */
    if ((err = ioctl(sockSetip, SIOCGIFFLAGS, &ifr)) < 0)
    {
        printf("ioctl SIOCGIFFLAGS ERR\n");
        close(sockSetip);
        close(tunFd);
        return err;
    }
    /* 虚拟网卡模式设置 */
    ifr.ifr_flags |= IFF_UP | IFF_RUNNING | IFF_PROMISC;
    if ((err = ioctl(sockSetip, SIOCSIFFLAGS, &ifr)) < 0)
    {
        printf("ioctl SIOCSIFFLAGS ERR\n");
        close(sockSetip);
        close(tunFd);
        return err;
    }
    /* 设置虚拟网卡地址 */
    memset(&si, 0, sizeof(struct sockaddr_in));
    si.sin_family = AF_INET;
    si.sin_addr.s_addr = aLocalAddr;
    memcpy(&ifr.ifr_addr, &si, sizeof(struct sockaddr_in));
    if ((err = ioctl(sockSetip, SIOCSIFADDR, &ifr)) < 0)
    {
        printf("Failed to set ip address\n");
        close(sockSetip);
        close(tunFd);
        return err;
    }

    si.sin_addr.s_addr = aNetMask;
    memcpy(&ifr.ifr_netmask, &si, sizeof(struct sockaddr_in));
    if ((err = ioctl(sockSetip, SIOCSIFNETMASK, &ifr)) < 0)
    {
        printf("Failed to set netmask\n");
        close(sockSetip);
        close(tunFd);
        return err;
    }
    close(sockSetip);
    return tunFd;
}

主函数

int main()
{
    char readBuf[WBUFF_SIZE];
    memset(readBuf, 0, WBUFF_SIZE);
    int tFd, ret;
    char mDev[IFNAMSIZ] = "TEST0";
    
    tFd = initTun(mDev, inet_addr("192.168.10.1"), inet_addr("255.255.255.252"));
    if (tFd < 0)
    {
        printf("Failed to open tun\n");
        return -1}

    while (1)
    {
        ret = read(tFd, readBuf, WBUFF_SIZE);
        if (ret > 48)
        {
            printf("read %d bytes\n", ret);
            printfHex(readBuf, ret);
            /* 修改IP头 */
            struct iphdr *iph = (struct iphdr *)readBuf;
            unsigned int iptemp;
            iptemp = iph->daddr;
            iph->daddr = iph->saddr;
            iph->saddr = iptemp;
            uint32_t iphSize = iph->ihl * 4;
            iph->check = 0;
            iph->check = ip_checksum((uint16_t *)iph, iphSize);
            /* 修改ICMP头 */
            struct icmphdr *icmph = (struct icmphdr *)(readBuf + iphSize);
            icmph->type = ICMP_ECHOREPLY;
            icmph->code = 0x00;
            icmph->checksum = 0x0000;
            icmph->checksum = icmp_checksum((unsigned char *)icmph, ret - iphSize);
            /* 返回Ping包 */
            ret = write(tFd, readBuf, ret);
            if (ret > 0)
            {
                printf("write %d bytes\n", ret);
                printfHex(readBuf, ret);
            }
        }
    }
    close(tFd);
    return 0;
}

效果

发起ping命令终端成功获取回包
主程序打印的read()读取数据(十六进制)write()读写数据(十六进制)

补充引用的头文件

#include <sys/ioctl.h>
#include <linux/ip.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_ether.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/icmp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个错误通常表示在编译`aqua-sim-routing-dummy.cc`文件时出现了问题。根据日志,这是通过使用g++编译器尝试编译该文件的命令: ```shell /usr/bin/g++ -O0 -ggdb -g3 -Wall -Werror -std=c++11 -Wno-error=deprecated-declarations -fstrict-aliasing -Wstrict-aliasing -fPIC -pthread -I. -I.. -DNS3_BUILD_PROFILE_DEBUG -DNS3_ASSERT_ENABLE -DNS3_LOG_ENABLE -DHAVE_SYS_IOCTL_H=1 -DHAVE_IF_NETS_H=1 -DHAVE_NET_ETHERNET_H=1 -DHAVE_PACKET_H=1 -DHAVE_IF_TUN_H=1 -DHAVE_GSL=1 -DHAVE_SQLITE3=1 ../src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc -c -o /home/fjl/ns-allinone-3.26/ns-3.26/build/src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc.1.o ``` 根据命令和错误信息,可能有以下一些原因导致构建失败: 1. 缺少某些依赖项:请确保你的系统中安装了所有必需的依赖项。在这个特定的情况下,确保你的系统中安装了g++编译器、pthread库、以及其他相关的头文件和库。 2. 编译器错误:有时候编译器可能会有一些问题,尝试更新或更换你的编译器版本,可能会解决该问题。 3. 文件路径错误:检查`../src/aqua-sim-ng/model/aqua-sim-routing-dummy.cc`文件是否存在,并确保路径正确。 4. 代码错误:在`aqua-sim-routing-dummy.cc`文件中可能存在语法错误或其他编译错误。请仔细检查该文件,并确保代码没有问题。 希望这些提示能够帮助你解决构建失败的问题!如果还有其他疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值