Better daemonization properties.
[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 [-hdp] [-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 int main(int argc, char **argv)
208 {
209     int c;
210     int sock, tap;
211     struct in_addr group;
212     int port;
213     char *tapname;
214     char *pidfile;
215     int daemonize;
216     struct sockaddr_in dst;
217     FILE *pidfd;
218     
219     tapname = "mctap";
220     daemonize = 0;
221     pidfile = NULL;
222     while((c = getopt(argc, argv, "hD:dpP:")) >= 0) {
223         switch(c) {
224         case 'D':
225             tapname = optarg;
226             break;
227         case 'd':
228             daemonize = 1;
229             break;
230         case 'p':
231             pidfile = (void *)-1;
232             break;
233         case 'P':
234             pidfile = optarg;
235             break;
236         case 'h':
237             usage(stdout);
238             return(0);
239         default:
240             usage(stderr);
241             exit(1);
242         }
243     }
244     if(pidfile == (void *)-1)
245         pidfile = sprintf2("/var/run/mctap.%s.pid", tapname);
246     if(argc - optind < 2) {
247         usage(stderr);
248         exit(1);
249     }
250     if(!inet_aton(argv[optind], &group)) {
251         fprintf(stderr, "mctap: invalid group address: %s\n", argv[optind]);
252         exit(1);
253     }
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));
257         exit(1);
258     }
259     if((tap = maketap(tapname)) < 0) {
260         fprintf(stderr, "mctap: could not create TAP device: %s\n", strerror(errno));
261         exit(1);
262     }
263     openlog(sprintf2("mctap-%s", tapname), LOG_PID, LOG_DAEMON);
264     
265     pidfd = NULL;
266     if((pidfile != NULL) && ((pidfd = fopen(pidfile, "w")) == NULL)) {
267         fprintf(stderr, "mctap: could not create PID file %s: %s\n", pidfile, strerror(errno));
268         exit(1);
269     }
270     if(daemonize)
271         daemon(0, 0);
272     if(pidfd != NULL) {
273         fprintf(pidfd, "%i\n", getpid());
274         fclose(pidfd);
275     }
276     
277     signal(SIGTERM, sighand);
278     signal(SIGINT, sighand);
279     signal(SIGHUP, sighand);
280     
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");
287     
288     if(pidfile != NULL)
289         unlink(pidfile);
290     return(0);
291 }