5 #include <sys/socket.h>
6 #include <netinet/in.h>
10 #include <linux/if_tun.h>
12 #include <sys/ioctl.h>
21 static void usage(FILE *out)
23 fprintf(out, "usage: mctap [-hdp] [-P PIDFILE] [-D TAPNAME] MCASTGROUP PORT\n");
26 static __attribute__ ((unused)) char *formataddress(struct sockaddr *arg, socklen_t arglen)
28 struct sockaddr_in *ipv4;
29 struct sockaddr_in6 *ipv6;
30 static char *ret = NULL;
36 switch(arg->sa_family)
39 ret = sstrdup("Unix socket");
42 ipv4 = (struct sockaddr_in *)arg;
43 if(inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)) == NULL)
45 ret = sprintf2("%s:%i", buf, (int)ntohs(ipv4->sin_port));
48 ipv6 = (struct sockaddr_in6 *)arg;
49 if(inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)) == NULL)
51 ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port));
60 static int mkmcastsk4(struct in_addr group, int port)
64 struct sockaddr_in nm;
67 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
69 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &soval, sizeof(soval)))
71 memset(&nm, 0, sizeof(nm));
72 nm.sin_family = AF_INET;
73 nm.sin_port = htons(port);
74 if(bind(fd, (struct sockaddr *)&nm, sizeof(nm)))
76 memset(&mreq, 0, sizeof(mreq));
77 mreq.imr_multiaddr = group;
78 if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)))
81 if(setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &soval, sizeof(soval)))
86 static __attribute__ ((unused)) void test(int fd)
95 ret = poll(&pfd, 1, -1);
97 fprintf(stderr, "mctap: poll: %s\n", strerror(errno));
101 ret = read(fd, buf, sizeof(buf));
103 fprintf(stderr, "mctap: read: %s\n", strerror(errno));
106 for(i = 0; i < ret; i++) {
107 printf("%02x ", (unsigned char)buf[i]);
116 static void bridge(int sock, int tap, struct sockaddr *dst, socklen_t dstlen)
120 struct pollfd pfds[2];
123 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
124 fcntl(tap, F_SETFL, fcntl(tap, F_GETFL) | O_NONBLOCK);
127 pfds[0].events = POLLIN;
129 pfds[1].events = POLLIN;
130 ret = poll(pfds, 2, -1);
133 syslog(LOG_ERR, "mctap: poll: %s", strerror(errno));
138 if(pfds[0].revents) {
139 ret = read(sock, buf, sizeof(buf));
141 if((errno != EINTR) && (errno != EAGAIN)) {
142 syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
146 if(sizeof(buf) - ret < sizeof(pi)) {
149 memmove(buf + sizeof(pi), buf, ret);
152 memcpy(buf, &pi, sizeof(pi));
153 write(tap, buf, sizeof(pi) + ret);
157 if(pfds[1].revents) {
158 ret = read(tap, buf, sizeof(buf));
160 if((errno != EINTR) && (errno != EAGAIN)) {
161 syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
165 if(ret < sizeof(pi)) {
168 memcpy(&pi, buf, sizeof(pi));
169 if(pi.flags & TUN_PKT_STRIP) {
172 sendto(sock, buf + sizeof(pi), ret - sizeof(pi), 0, dst, dstlen);
180 static int maketap(char *name)
185 if((fd = open("/dev/net/tun", O_RDWR)) < 0)
187 memset(&rb, 0, sizeof(rb));
188 rb.ifr_flags = IFF_TAP;
189 strncpy(rb.ifr_name, name, IFNAMSIZ);
190 if(ioctl(fd, TUNSETIFF, &rb))
195 static void sighand(int sig)
207 int main(int argc, char **argv)
211 struct in_addr group;
216 struct sockaddr_in dst;
222 while((c = getopt(argc, argv, "hD:dpP:")) >= 0) {
231 pidfile = (void *)-1;
244 if(pidfile == (void *)-1)
245 pidfile = sprintf2("/var/run/mctap.%s.pid", tapname);
246 if(argc - optind < 2) {
250 if(!inet_aton(argv[optind], &group)) {
251 fprintf(stderr, "mctap: invalid group address: %s\n", argv[optind]);
254 port = atoi(argv[optind + 1]);
255 if((sock = mkmcastsk4(group, port)) < 0) {
256 fprintf(stderr, "mctap: could not create multicast socket: %s\n", strerror(errno));
259 if((tap = maketap(tapname)) < 0) {
260 fprintf(stderr, "mctap: could not create TAP device: %s\n", strerror(errno));
263 openlog(sprintf2("mctap-%s", tapname), LOG_PID, LOG_DAEMON);
266 if((pidfile != NULL) && ((pidfd = fopen(pidfile, "w")) == NULL)) {
267 fprintf(stderr, "mctap: could not create PID file %s: %s\n", pidfile, strerror(errno));
273 fprintf(pidfd, "%i\n", getpid());
277 signal(SIGTERM, sighand);
278 signal(SIGINT, sighand);
279 signal(SIGHUP, sighand);
281 memset(&dst, 0, sizeof(dst));
282 dst.sin_family = AF_INET;
283 dst.sin_addr = group;
284 dst.sin_port = htons(port);
285 bridge(sock, tap, (struct sockaddr *)&dst, sizeof(dst));
286 syslog(LOG_INFO, "exiting");