La question concerne la configuration appropriée d’un hôte Linux qui souhaite utiliser le module Tun / Tap.
Mon but:
Utiliser un logiciel de routage existant (APP1 et APP2 dans la suite) mais intercepter et modifier tous les messages envoyés et reçus par celui-ci (effectué par le médiateur).
Mon scénario:
Ubuntu 10.04 Machine +---------------------------------------------+ | | |APP1 --- tap1 --- Mediator --- tap2 --- APP2 | | | +---------------------------------------------+
tap1 et tap2: tapez la configuration des appareils avec le drapeau IFF_TAP et les adresses IP 10.0.0.1/24 et 10.0.0.2/24 respectivement. Le code pour créer les périphériques est le suivant:
#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; }
Mediator: petit code auto-écrit permettant simplement de relayer des données entre tap1 et tap2. la structure de base est la suivante:
#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 et APP2: démons de routage OSPF communiquant via tap1 et tap2 respectivement. Une série de démons montre que les appels système suivants sont impliqués:
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) ...
Mon utilisation:
Mon problème:
Même si wirehark – attaché à tap1 – voit les messages de tap1 et tap2, APP2 ne reçoit pas les messages envoyés par APP1 et APP2 ne reçoit pas non plus les messages de APP1. Dans l’extrait strace présenté ci-dessus, l’appel select () ne renvoie jamais le descripteur de fichier 8, qui serait en réalité le socket connecté à tap1.
Mes questions:
Pourquoi APP1 ne reçoit-il pas les messages envoyés par APP2 alors que ces messages sont envoyés par APP2, relayés par le médiateur et vus par la connexion filaire attachée à tap1?
Dois-je append un type / type d’itinéraires supplémentaires sur mon hôte Linux?
Ai-je commis une erreur lors de la configuration des appareils tun / tap?
Mon code Mediator ne fonctionne-t-il pas correctement?
Je n’ai pas essayé votre code (il est un peu étrange que vous ayez pu ouvrir deux fois un périphérique TAP depuis un espace utilisateur sans utiliser d’indicateur de queue multiple , mais supposons que c’est correct), mais vous avez une erreur conceptuelle dans la façon dont vous gérez les périphériques TAP. .
Qu’est-ce que TUN / TAP est essentiellement juste un tuyau, un côté de ce tuyau est dans le kernel (l’interface tapX) et l’autre dans certaines applications de l’espace utilisateur. Tout ce que cette application écrit dans le tube est acheminé vers l’interface du kernel sous forme de trafic entrant (et vous le voyez avec Warshark). Quel que soit le kernel envoyé à ce canal (sortant vers tapX) finit par arriver dans l’application (les données que vous pouvez lire dans application).
Votre code est en train d’ouvrir une autre partie de la même conduite dans l’espace utilisateur, et ce n’est pas ce que vous voulez. Vous voulez obtenir du trafic de l’autre côté du tuyau. Techniquement, ce que vous faites actuellement pourrait être fait par une simple interface de pont avec les deux taps ajoutés comme ports. Bien sûr, si vous souhaitez non seulement créer un pont, mais également modifier le trafic, les choses se compliquent un peu.
Une façon de résoudre ce problème consiste à append une autre paire d’interfaces TAP. Vous pontez (comme dans le kernel) votre tap1 avec tap3 et tap2 avec tap4, maintenant vous ouvrez tap3 et tap4 dans vos frameworks “médiateur” et proxy entre eux. Ceci est terriblement inefficace, mais peut être une solution à votre problème.