Linux TUN / TAP:无法从TAP设备读回数据

 手机用户2402851335 发布于 2023-02-05 10:08

问题是有关要利用Tun / Tap模块的Linux主机的正确配置。

我的目标:

利用现有的路由软件(以下简称APP1和APP2),但截取和修改由它发送和接收的所有消息(由Mediator完成)。

我的场景:

              Ubuntu 10.04 Machine
+---------------------------------------------+
|                                             |
|APP1 --- tap1 --- Mediator --- tap2 --- APP2 |
|                                             |
+---------------------------------------------+

tap1和tap2:分别设置带有IFF_TAP标志和IP 10.0.0.1/24和10.0.0.2/24的设备设置。创建设备的代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void createTun(char *, char *, short);

int main(void)
{
    const short FLAGS = IFF_TAP;
    char *tunName;
    char *tunIP;

    // Create tap1
    tunName = "tap1\0";
    tunIP = "10.0.0.1/24\0";
    createTun(tunName, tunIP, FLAGS);
    printf("Created %s with IP %s\n", tunName, tunIP);

    // Create tap2
    tunName = "tap2\0";
    tunIP = "10.0.0.2/24\0";
    createTun(tunName, tunIP, FLAGS);
    printf("Created %s with IP %s\n", tunName, tunIP);

    return 0;
}

void createTun(char *tunName, char *tunIP, short FLAGS)
{
    char *cmd;
    char *cloneDev = "/dev/net/tun";
    char *cmdIPLinkUpTemplate = "ip link set %s up";
    char *cmdIPAddrAddTemplate = "ip addr add %s dev %s";
    int cmdIPLinkUpRawLength = strlen(cmdIPLinkUpTemplate) - 2;
    int cmdIPAddrAddRawLength = strlen(cmdIPAddrAddTemplate) - 4;
    FILE *fp;
    int fd, err, owner, group;
    struct ifreq ifr;

    owner = geteuid();
    group = getegid();

    // open the clone device
    if((fd = open(cloneDev, O_RDWR)) < 0)
    {
        perror("OPEN CLONEDEV failed.");
        exit(EXIT_FAILURE);
    }

    memset(&ifr, 0, sizeof(struct ifreq));
    ifr.ifr_flags = FLAGS;
    strncpy(ifr.ifr_name, tunName, strlen(tunName));

    // create the device
    if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0)
    {
        perror("IOCTL SETIFF denied.");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // set dev owner
    if(owner != -1)
    {
    if(ioctl(fd, TUNSETOWNER, owner) < 0)
    {
        perror("IOCTL SETOWNER denied.");
        close(fd);
        exit(EXIT_FAILURE);
    }
    }

    // set dev group
    if(group != -1)
    {
    if(ioctl(fd, TUNSETGROUP, group) < 0)
    {
        perror("IOCTL SETGROUP denied.");
        close(fd);
        exit(EXIT_FAILURE);
    }
    }

    // set dev persistent
    if(ioctl(fd, TUNSETPERSIST, 1) < 0)
    {
        perror("IOCTL SETPERSIST denied.");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // Set dev up
    cmd = malloc(cmdIPLinkUpRawLength + strlen(tunName) + 1);
    sprintf(cmd, cmdIPLinkUpTemplate, ifr.ifr_name);
    fp = popen(cmd, "r");
    if(fp == NULL)
    {
        perror("POPEN failed.");
        close(fd);
        free(cmd);
        exit(EXIT_FAILURE);
    }
    pclose(fp);
    free(cmd);

    // Assign IP
    cmd = malloc(cmdIPAddrAddRawLength + strlen(tunIP) + strlen(tunName) + 1);
    sprintf(cmd, cmdIPAddrAddTemplate, tunIP, tunName);
    fp = popen(cmd, "r");
    if(fp == NULL)
    {
        perror("POPEN failed.");
        close(fd);
        free(cmd);
        exit(EXIT_FAILURE);
    }

    pclose(fp);
    free(cmd);

    return;
}

介体:小的自编写代码,可以在tap1和tap2之间简单地中继数据。基本结构如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    const int NOF_FD = 2;
    const char *TUN1 = "tap1";
    const char *TUN2 = "tap2";
    const char *CLONEDEV = "/dev/net/tun";
    int fd_tun1, fd_tun2, fd_epoll;
    struct ifreq ifr_tun1, ifr_tun2;
    struct epoll_event ev;
    const int MAX_EVENTS = 1;
    int ready, s, t;
    const int MAX_BUF = 2000;
    char buf[MAX_BUF];
    struct sockaddr_in to;
    const short FLAGS = IFF_TAP;

    // Open tap1
    if((fd_tun1 = open(CLONEDEV, O_RDWR)) < 0)
    {
        perror("OPEN CLONEDEV for tun1 failed");
        exit(EXIT_FAILURE);
    }

    memset(&ifr_tun1, 0, sizeof(struct ifreq));
    ifr_tun1.ifr_flags = FLAGS;
    strcpy(ifr_tun1.ifr_name, TUN1);
    if(ioctl(fd_tun1, TUNSETIFF, (void *) &ifr_tun1) < 0)
    {
        perror("IOCTL SETIFF for tap1 failed");
        close(fd_tun1);
        exit(EXIT_FAILURE);
    }

    // Open tap2
    if((fd_tun2 = open(CLONEDEV, O_RDWR)) < 0)
    {
        perror("OPEN CLONEDEV for tap2 failed");
        exit(EXIT_FAILURE);
    }

    memset(&ifr_tun2, 0, sizeof(struct ifreq));
    ifr_tun2.ifr_flags = FLAGS;
    strcpy(ifr_tun2.ifr_name, TUN2);
    if(ioctl(fd_tun2, TUNSETIFF, (void *) &ifr_tun2) < 0)
    {
        perror("IOCTL SETIFF for tun2 failed");
        close(fd_tun1);
        close(fd_tun2);
        exit(EXIT_FAILURE);
    }

    // Prepare EPOLL
    if((fd_epoll = epoll_create(NOF_FD)) < 0)
    {
        perror("EPOLL CREATE failed");
        close(fd_tun1);
        close(fd_tun2);
        exit(EXIT_FAILURE);
    }

    memset(&ev, 0, sizeof(ev));
    ev.events = EPOLLIN;
    ev.data.fd = fd_tun1;
    if(epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_tun1, &ev) < 0)
    {
        perror("EPOLL CTL ADD fd_tun1 failed");
        close(fd_tun1);
        close(fd_tun2);
        close(fd_epoll);
        exit(EXIT_FAILURE);
    }

    memset(&ev, 0, sizeof(ev));
    ev.events = EPOLLIN;
    ev.data.fd = fd_tun2;
    if(epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd_tun2, &ev) < 0)
    {
        perror("EPOLL CTL ADD fd_tun2 failed");
        close(fd_tun1);
        close(fd_tun2);
        close(fd_epoll);
        exit(EXIT_FAILURE);
    }

    // Do relay
    while(1)
    {
        if((ready = epoll_wait(fd_epoll, &ev, MAX_EVENTS, -1)) < 0)
        {
            if(errno == EINTR)
                continue;
            else
            {
                perror("EPOLL WAIT failed");
                close(fd_tun1);
                close(fd_tun2);
                close(fd_epoll);
                exit(EXIT_FAILURE);
            }
        }

        //printf("EPOLL WAIT SIGNALED\n");

        if(ev.events & EPOLLIN)
        {
            if((s = read(ev.data.fd, buf, MAX_BUF)) < 0)
            {
                perror("READ failed");
                close(fd_tun1);
                close(fd_tun2);
                close(fd_epoll);
                exit(EXIT_FAILURE);
            }

            printf("Read from %s. Bytes: %d\nData:\n", (ev.data.fd == fd_tun1 ? "tun1" : "tun2"), s);
            int k;
            for(k = 0; k < s; k++)
            {
                printf("%c", buf[k]);
            }
            printf("\n");

            t = (ev.data.fd == fd_tun1) ? fd_tun2 : fd_tun1;

            if((s = write(t, buf, s)) < 0)
            {
                perror("WRITE failed");
                close(fd_tun1);
                close(fd_tun2);
                close(fd_epoll);
                exit(EXIT_FAILURE);
            }

            printf("Written to %s. Bytes: %d\n", (t == fd_tun1 ? "tun1" : "tun2"), s);

            if(epoll_ctl(fd_epoll, EPOLL_CTL_DEL, ev.data.fd, NULL) < 0)
            {
                perror("EPOLL CTL DEL failed");
                close(fd_tun1);
                close(fd_tun2);
                close(fd_epoll);
                exit(EXIT_FAILURE);
            }

            if(epoll_ctl(fd_epoll, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
            {
                perror("EPOLL CTL ADD failed");
                close(fd_tun1);
                close(fd_tun2);
                close(fd_epoll);
                exit(EXIT_FAILURE);
            }
        }

        printf("\n\n");
    }
}

APP1和APP2:OSPF路由守护程序分别通过tap1和tap2进行通信。守护程序的痕迹表明,基本上涉及以下系统调用:

socket(PF_INET, SOCK_RAW, 0X59 /*IPPROTO_??? */) = 8 // Opening a socket for OSPF and tap1
fcntl64(8, F_SETFL, 0_RDONLY | 0_NONBLOCK) = 0
setsockopt(8, SOL_IP, IP_TOS, [192], 4) = 0
setsockopt(8, SOL_SOCKET, SO_PRIORITY, [7], 4) = 0
setsockopt(8, SOL_IP, IP_PKTINFO, [1], 4) = 0
setsockopt(8, SOL_IP, IP_MTU_DISCOVER, [0], 4) = 0
setsockopt(8, SOL_IP, IP_MULTICAST_LOOP, [0], 4) = 0
setsockopt(8, SOL_IP, IP_MULTICAST_TTL, [1], 4) = 0
setsockopt(8, SOL_IP, IP_MUTLICAST_IF, "\0\0\0\0\n\0\0\1\223\0\0\0", 12) = 0
setsockopt(8, SOL_SOCKET, SO_BINDTODEVICE, "tap1\0\0\0\0\0\0\0\0\0\0\0\0\0\315\375\307\250\352\t\t8\207\t\10\0\0\0\0", 32) = 0
setsockopt(8, SOL_IP, IP_ADD_MEMBERSHIP, "340\0\0\5\n\0\0\1\223\0\0\0", 12) = 0

// Then it gets in a cycle like:
select(9, [3, 7, 8], [], NULL, {1, 0}) = 0 (Timeout)
clock_gettime(CLOCK_MONOTONIC, {120893, 360452769}) = 0
time(NULL)
clock_gettime(CLOCK_MONOTONIC, {120893, 360504525}) = 0
select(9, [3, 7, 8], [], NULL, {1, 0}) = 0 (Timeout)
clock_gettime(CLOCK_MONOTONIC, {120894, 363022746}) = 0
time(NULL)
...

我的用法:

将Wireshk连接到tap1。(尚未看到流量)。

启动APP1。(wireshark看到源为10.0.0.1(tap1)的IGMP和OSPF消息)

启动APP2。(由于Mediator尚未运行,wireshark仍然仅看到源10.0.0.1(tap1)的IGMP和OSPF消息)

启动调解器。(wireshark现在可以看到源为tap1和tap2的IGMP和OSPF消息)。

我的问题:

即使连接到Tap1的Wireshk可以同时看到Tap1和Tap2的消息,APP2也不会接收APP1发送的消息,APP2也不会接收来自APP1的消息。在上面显示的strace提取中,select()调用从不返回文件描述符8,该文件描述符实际上是连接到tap1的套接字。

我的问题:

即使APP1发送了这些消息,并由Mediator中继并由tap1附带的Wireshark看到了,APP1为什么仍未收到APP2发送的消息?

我是否需要在Linux主机上添加任何类型/种类的其他路由?

设置调音/攻丝设备时是否出错?

我的调解员代码不能正常工作吗?

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有