9d183f983e1340a1c755bf93c022d377b353638d
[mctap.git] / src / mctap.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <errno.h>
9 #include <net/if.h>
10 #include <linux/if_tun.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #include <sys/poll.h>
14 #include <syslog.h>
15 #include <signal.h>
16
17 #include "utils.h"
18
19 static int quit = 0;
20
21 static void usage(FILE *out)
22 {
23     fprintf(out, "usage: mctap [-hdpk] [-P PIDFILE] [-D TAPNAME] MCASTGROUP PORT\n");
24 }
25
26 static __attribute__ ((unused)) char *formataddress(struct sockaddr *arg, socklen_t arglen)
27 {
28     struct sockaddr_in *ipv4;
29     struct sockaddr_in6 *ipv6;
30     static char *ret = NULL;
31     char buf[1024];
32     
33     if(ret != NULL)
34         free(ret);
35     ret = NULL;
36     switch(arg->sa_family)
37     {
38     case AF_UNIX:
39         ret = sstrdup("Unix socket");
40         break;
41     case AF_INET:
42         ipv4 = (struct sockaddr_in *)arg;
43         if(inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)) == NULL)
44             return(NULL);
45         ret = sprintf2("%s:%i", buf, (int)ntohs(ipv4->sin_port));
46         break;
47     case AF_INET6:
48         ipv6 = (struct sockaddr_in6 *)arg;
49         if(inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)) == NULL)
50             return(NULL);
51         ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port));
52         break;
53     default:
54         errno = EPFNOSUPPORT;
55         break;
56     }
57     return(ret);
58 }
59
60 static int mkmcastsk4(struct in_addr group, int port)
61 {
62     int fd;
63     int soval;
64     struct sockaddr_in nm;
65     struct ip_mreqn mreq;
66     
67     fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
68     soval = 1;
69     if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &soval, sizeof(soval)))
70         return(-1);
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)))
75         return(-1);
76     memset(&mreq, 0, sizeof(mreq));
77     mreq.imr_multiaddr = group;
78     if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)))
79         return(-1);
80     soval = 1;
81     if(setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &soval, sizeof(soval)))
82         return(-1);
83     return(fd);
84 }
85
86 static __attribute__ ((unused)) void test(int fd)
87 {
88     char buf[65536];
89     int i, ret;
90     struct pollfd pfd;
91     
92     while(1) {
93         pfd.fd = fd;
94         pfd.events = POLLIN;
95         ret = poll(&pfd, 1, -1);
96         if(ret < 0) {
97             fprintf(stderr, "mctap: poll: %s\n", strerror(errno));
98             exit(1);
99         }
100         if(pfd.revents) {
101             ret = read(fd, buf, sizeof(buf));
102             if(ret < 0) {
103                 fprintf(stderr, "mctap: read: %s\n", strerror(errno));
104                 exit(1);
105             }
106             for(i = 0; i < ret; i++) {
107                 printf("%02x ", (unsigned char)buf[i]);
108                 if(i % 20 == 19)
109                     putchar(10);
110             }
111             putchar(10);
112         }
113     }
114 }
115
116 static void bridge(int sock, int tap, struct sockaddr *dst, socklen_t dstlen)
117 {
118     char buf[65536];
119     int ret;
120     struct pollfd pfds[2];
121     struct tun_pi pi;
122     
123     fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
124     fcntl(tap, F_SETFL, fcntl(tap, F_GETFL) | O_NONBLOCK);
125     while(!quit) {
126         pfds[0].fd = sock;
127         pfds[0].events = POLLIN;
128         pfds[1].fd = tap;
129         pfds[1].events = POLLIN;
130         ret = poll(pfds, 2, -1);
131         if(ret < 0) {
132             if(errno != EINTR) {
133                 syslog(LOG_ERR, "mctap: poll: %s", strerror(errno));
134                 exit(1);
135             }
136             continue;
137         }
138         if(pfds[0].revents) {
139             ret = read(sock, buf, sizeof(buf));
140             if(ret < 0) {
141                 if((errno != EINTR) && (errno != EAGAIN)) {
142                     syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
143                     exit(1);
144                 }
145             } else {
146                 if(sizeof(buf) - ret < sizeof(pi)) {
147                     /* Drop */
148                 } else {
149                     memmove(buf + sizeof(pi), buf, ret);
150                     pi.flags = 0;
151                     pi.proto = 0;
152                     memcpy(buf, &pi, sizeof(pi));
153                     write(tap, buf, sizeof(pi) + ret);
154                 }
155             }
156         }
157         if(pfds[1].revents) {
158             ret = read(tap, buf, sizeof(buf));
159             if(ret < 0) {
160                 if((errno != EINTR) && (errno != EAGAIN)) {
161                     syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
162                     exit(1);
163                 }
164             } else {
165                 if(ret < sizeof(pi)) {
166                     /* Drop */
167                 } else {
168                     memcpy(&pi, buf, sizeof(pi));
169                     if(pi.flags & TUN_PKT_STRIP) {
170                         /* Drop */
171                     } else {
172                         sendto(sock, buf + sizeof(pi), ret - sizeof(pi), 0, dst, dstlen);
173                     }
174                 }
175             }
176         }
177     }
178 }
179
180 static int maketap(char *name)
181 {
182     int fd;
183     struct ifreq rb;
184     
185     if((fd = open("/dev/net/tun", O_RDWR)) < 0)
186         return(-1);
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))
191         return(-1);
192     return(fd);
193 }
194
195 static void sighand(int sig)
196 {
197     switch(sig) {
198     case SIGINT:
199     case SIGTERM:
200         quit = 1;
201         break;
202     case SIGHUP:
203         break;
204     }
205 }
206
207 static void killrunning(char *pidfile)
208 {
209     FILE *pidfd;
210     int pid;
211     
212     if((pidfd = fopen(pidfile, "r")) == NULL) {
213         fprintf(stderr, "mctab -k: could not read PID file %s: %s\n", pidfile, strerror(errno));
214         exit(1);
215     }
216     fscanf(pidfd, "%i", &pid);
217     if(kill(pid, SIGTERM)) {
218         fprintf(stderr, "mctab -k: could not kill %i: %s\n", pid, strerror(errno));
219         exit(1);
220     }
221     fclose(pidfd);
222 }
223
224 int main(int argc, char **argv)
225 {
226     int c;
227     int sock, tap;
228     struct in_addr group;
229     int port;
230     char *tapname;
231     char *pidfile;
232     int daemonize, killold;
233     struct sockaddr_in dst;
234     FILE *pidfd;
235     
236     tapname = "mctap";
237     daemonize = killold = 0;
238     pidfile = NULL;
239     while((c = getopt(argc, argv, "hD:dpP:k")) >= 0) {
240         switch(c) {
241         case 'D':
242             tapname = optarg;
243             break;
244         case 'd':
245             daemonize = 1;
246             break;
247         case 'p':
248             pidfile = (void *)-1;
249             break;
250         case 'P':
251             pidfile = optarg;
252             break;
253         case 'k':
254             killold = 1;
255             if(pidfile == NULL)
256                 pidfile = (void *)-1;
257             break;
258         case 'h':
259             usage(stdout);
260             return(0);
261         default:
262             usage(stderr);
263             exit(1);
264         }
265     }
266     if(pidfile == (void *)-1)
267         pidfile = sprintf2("/var/run/mctap.%s.pid", tapname);
268     if(killold) {
269         killrunning(pidfile);
270         return(0);
271     }
272     if(argc - optind < 2) {
273         usage(stderr);
274         exit(1);
275     }
276     if(!inet_aton(argv[optind], &group)) {
277         fprintf(stderr, "mctap: invalid group address: %s\n", argv[optind]);
278         exit(1);
279     }
280     port = atoi(argv[optind + 1]);
281     if((sock = mkmcastsk4(group, port)) < 0) {
282         fprintf(stderr, "mctap: could not create multicast socket: %s\n", strerror(errno));
283         exit(1);
284     }
285     if((tap = maketap(tapname)) < 0) {
286         fprintf(stderr, "mctap: could not create TAP device: %s\n", strerror(errno));
287         exit(1);
288     }
289     openlog(sprintf2("mctap-%s", tapname), LOG_PID, LOG_DAEMON);
290     
291     pidfd = NULL;
292     if((pidfile != NULL) && ((pidfd = fopen(pidfile, "w")) == NULL)) {
293         fprintf(stderr, "mctap: could not create PID file %s: %s\n", pidfile, strerror(errno));
294         exit(1);
295     }
296     if(daemonize)
297         daemon(0, 0);
298     if(pidfd != NULL) {
299         fprintf(pidfd, "%i\n", getpid());
300         fclose(pidfd);
301     }
302     
303     signal(SIGTERM, sighand);
304     signal(SIGINT, sighand);
305     signal(SIGHUP, sighand);
306     
307     memset(&dst, 0, sizeof(dst));
308     dst.sin_family = AF_INET;
309     dst.sin_addr = group;
310     dst.sin_port = htons(port);
311     bridge(sock, tap, (struct sockaddr *)&dst, sizeof(dst));
312     syslog(LOG_INFO, "exiting");
313     
314     if(pidfile != NULL)
315         unlink(pidfile);
316     return(0);
317 }