问题是有关要利用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主机上添加任何类型/种类的其他路由?
设置调音/攻丝设备时是否出错?
我的调解员代码不能正常工作吗?