2014-01-08 21 views
3

問題在於如何正確配置想要使用Tun/Tap模塊的Linux主機。Linux TUN/TAP:無法從TAP設備讀回數據

我的目標:

利用現有的路由軟件的(APP1和APP2在下面),但攔截和modifiying通過它發送和接收的所有郵件(由中保完成)。

我的情景:

   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 <stdlib.h> 
    #include <stdio.h> 
    #include <sys/socket.h> 
    #include <sys/ioctl.h> 
    #include <fcntl.h> 
    #include <linux/if.h> 
    #include <linux/if_tun.h> 
    #include <string.h> 
    #include <errno.h> 
    #include <sys/resource.h> 
    
    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 <unistd.h> 
    #include <stdio.h> 
    #include <sys/socket.h> 
    #include <netinet/ip.h> 
    #include <sys/ioctl.h> 
    #include <sys/resource.h> 
    #include <sys/epoll.h> 
    #include <errno.h> 
    #include <fcntl.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <linux/if.h> 
    #include <linux/if_tun.h> 
    
    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通信。守護進程的strace的顯示,基本上下面的系統調用涉及:

    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) 
    ... 
    

我的用法:

  • 安裝Wireshark來TAP1。 (沒有看到流量)。
  • 開始APP1。 (wireshark在源10.0.0.1(tap1)中看到IGMP和OSPF消息)
  • 啓動APP2。 (自從Mediator還沒有運行之後,wireshark仍然只能看到帶有源10.0.0.1(tap1)的IGMP和OSPF消息)
  • Start Mediator。 (wireshark現在可以看到帶有tap1和tap2源的IGMP和OSPF消息)。

我的問題:

即使Wireshark的 - 連接到TAP1 - 看到來自TAP1和TAP2消息,APP2不接收APP1發送的消息也不認爲APP2從APP1接收消息。在上面顯示的strace抽取中,select()調用永遠不會返回文件描述符8,實際上它將是連接到tap1的套接字。

我的問題:

爲什麼APP1無法收到信息的APP2發送即使這些消息是由APP 2發送,轉發由中保和附加到TAP1 Wireshark的見過?

我必須在Linux主機上添加任何類型/種類的附加路由嗎?

我在設置tun/tap設備時犯了錯誤嗎?

我的調解員代碼無法正常工作嗎?

+0

您是否可以將兩個進程附加到分流界面? – brainydexter

回答

0

我還沒有試過你的代碼(有點奇怪,你可以從用戶空間打開TAP設備兩次,而不是用戶空間不是using a multiqueue flag,但我們假設這是正確的),但是在處理TAP的方式中存在概念錯誤設備。

什麼TUN/TAP基本上只是一個管道,這個管道的一端在內核(tapX接口),另一端在某些用戶空間應用程序中。無論這個應用程序寫入管道到達內核接口作爲傳入流量(你可以用wireshark看到它)。無論內核發送到該管道(傳出到tapX)最終進入應用程序(您可以在應用程序中讀取的數據)。

您的代碼當前正在打開的是同一管道的另一個用戶空間部分,而這不是您想要的。你想在管道的另一端獲得交通。從技術上講,你目前正在做的事情可以通過一個簡單的橋接器來完成,並且兩個水龍頭都被添加爲端口。當然,如果你不想只是橋接,但以某種方式修改流量,事情會變得更加複雜。

解決此問題的一種方法是添加另一對TAP接口。您橋接(如內核橋接)您的tap1與tap3,tap2與tap4,現在您在「中介」中打開tap3和tap4,並在它們之間打開代理幀。這是非常低效的,但可能是解決您的問題。