Initial hopefully working code.
authorFredrik Tolf <fredrik@dolda2000.com>
Sat, 9 Aug 2008 15:26:59 +0000 (17:26 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Sat, 9 Aug 2008 15:26:59 +0000 (17:26 +0200)
src/mctap.c
src/utils.h

index 8125c41..472af01 100644 (file)
@@ -6,15 +6,20 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
 
 #include "utils.h"
 
 static void usage(FILE *out)
 {
-    fprintf(out, "usage: mctap [-h] MCASTGROUP PORT\n");
+    fprintf(out, "usage: mctap [-h] [-D TAPNAME] MCASTGROUP PORT\n");
 }
 
-static char *formataddress(struct sockaddr *arg, socklen_t arglen)
+static __attribute__ ((unused)) char *formataddress(struct sockaddr *arg, socklen_t arglen)
 {
     struct sockaddr_in *ipv4;
     struct sockaddr_in6 *ipv6;
@@ -27,7 +32,7 @@ static char *formataddress(struct sockaddr *arg, socklen_t arglen)
     switch(arg->sa_family)
     {
     case AF_UNIX:
-       ret = strdup("Unix socket");
+       ret = sstrdup("Unix socket");
        break;
     case AF_INET:
        ipv4 = (struct sockaddr_in *)arg;
@@ -74,38 +79,130 @@ static int mkmcastsk4(struct in_addr group, int port)
     return(fd);
 }
 
-static void test(int fd)
+static __attribute__ ((unused)) void test(int fd)
 {
     char buf[65536];
     int i, ret;
-    struct sockaddr_storage nm;
-    socklen_t nmlen;
+    struct pollfd pfd;
     
     while(1) {
-       nmlen = sizeof(nm);
-       ret = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&nm, &nmlen);
+       pfd.fd = fd;
+       pfd.events = POLLIN;
+       ret = poll(&pfd, 1, -1);
        if(ret < 0) {
-           fprintf(stderr, "mctap: recvfrom: %s\n", strerror(ret));
+           fprintf(stderr, "mctap: poll: %s\n", strerror(errno));
            exit(1);
        }
-       printf("%s %i:\n", formataddress((struct sockaddr *)&nm, nmlen), ret);
-       for(i = 0; i < ret; i++) {
-           printf("%02x ", (unsigned char)buf[i]);
-           if(i % 20 == 0)
-               putchar(10);
+       if(pfd.revents) {
+           ret = read(fd, buf, sizeof(buf));
+           if(ret < 0) {
+               fprintf(stderr, "mctap: read: %s\n", strerror(errno));
+               exit(1);
+           }
+           for(i = 0; i < ret; i++) {
+               printf("%02x ", (unsigned char)buf[i]);
+               if(i % 20 == 19)
+                   putchar(10);
+           }
+           putchar(10);
        }
     }
 }
 
+static void bridge(int sock, int tap, struct sockaddr *dst, socklen_t dstlen)
+{
+    char buf[65536];
+    int ret;
+    struct pollfd pfds[2];
+    struct tun_pi pi;
+    
+    fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
+    fcntl(tap, F_SETFL, fcntl(tap, F_GETFL) | O_NONBLOCK);
+    while(1) {
+       pfds[0].fd = sock;
+       pfds[0].events = POLLIN;
+       pfds[1].fd = tap;
+       pfds[1].events = POLLIN;
+       ret = poll(pfds, 2, -1);
+       if(ret < 0) {
+           if(errno != EINTR) {
+               fprintf(stderr, "mctap: poll: %s\n", strerror(errno));
+               exit(1);
+           }
+           continue;
+       }
+       if(pfds[0].revents) {
+           ret = read(sock, buf, sizeof(buf));
+           if(ret < 0) {
+               if((errno != EINTR) && (errno != EAGAIN)) {
+                   fprintf(stderr, "mctap: mcast packet: %s\n", strerror(errno));
+                   exit(1);
+               }
+           } else {
+               if(sizeof(buf) - ret < sizeof(pi)) {
+                   /* Drop */
+               } else {
+                   memmove(buf + sizeof(pi), buf, ret);
+                   pi.flags = 0;
+                   pi.proto = 0;
+                   memcpy(buf, &pi, sizeof(pi));
+                   write(tap, buf, ret);
+               }
+           }
+       }
+       if(pfds[1].revents) {
+           ret = read(tap, buf, sizeof(buf));
+           if(ret < 0) {
+               if((errno != EINTR) && (errno != EAGAIN)) {
+                   fprintf(stderr, "mctap: mcast packet: %s\n", strerror(errno));
+                   exit(1);
+               }
+           } else {
+               if(ret < sizeof(pi)) {
+                   /* Drop */
+               } else {
+                   memcpy(&pi, buf, sizeof(pi));
+                   if(pi.flags & TUN_PKT_STRIP) {
+                       /* Drop */
+                   } else {
+                       sendto(sock, buf + sizeof(pi), ret - sizeof(pi), 0, dst, dstlen);
+                   }
+               }
+           }
+       }
+    }
+}
+
+static int maketap(char *name)
+{
+    int fd;
+    struct ifreq rb;
+    
+    if((fd = open("/dev/net/tun", O_RDWR)) < 0)
+       return(-1);
+    memset(&rb, 0, sizeof(rb));
+    rb.ifr_flags = IFF_TAP;
+    strncpy(rb.ifr_name, name, IFNAMSIZ);
+    if(ioctl(fd, TUNSETIFF, &rb))
+       return(-1);
+    return(fd);
+}
+
 int main(int argc, char **argv)
 {
     int c;
-    int sock;
+    int sock, tap;
     struct in_addr group;
     int port;
+    char *tapname;
+    struct sockaddr_in dst;
     
-    while((c = getopt(argc, argv, "h")) >= 0) {
+    tapname = "mctap";
+    while((c = getopt(argc, argv, "hD:")) >= 0) {
        switch(c) {
+       case 'D':
+           tapname = optarg;
+           break;
        case 'h':
            usage(stdout);
            return(0);
@@ -127,7 +224,15 @@ int main(int argc, char **argv)
        fprintf(stderr, "mctap: could not create multicast socket: %s\n", strerror(errno));
        exit(1);
     }
+    if((tap = maketap(tapname)) < 0) {
+       fprintf(stderr, "mctap: could not create TAP device: %s\n", strerror(errno));
+       exit(1);
+    }
     
-    test(sock);
+    memset(&dst, 0, sizeof(dst));
+    dst.sin_family = AF_INET;
+    dst.sin_addr = group;
+    dst.sin_port = htons(port);
+    bridge(sock, tap, (struct sockaddr *)&dst, sizeof(dst));
     return(0);
 }
index 3a1c41a..8358244 100644 (file)
@@ -67,6 +67,11 @@ struct buffer {
     size_t s, d;
 };
 
+struct intbuf {
+    int *b;
+    size_t s, d;
+};
+
 struct charbuf {
     char *b;
     size_t s, d;