Better daemonization properties.
[mctap.git] / src / mctap.c
CommitLineData
3415540f
FT
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>
b5038e15
FT
7#include <arpa/inet.h>
8#include <errno.h>
49697920
FT
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>
a13b233b 14#include <syslog.h>
1fefa40d 15#include <signal.h>
b5038e15
FT
16
17#include "utils.h"
3415540f 18
1fefa40d
FT
19static int quit = 0;
20
3415540f
FT
21static void usage(FILE *out)
22{
1fefa40d 23 fprintf(out, "usage: mctap [-hdp] [-P PIDFILE] [-D TAPNAME] MCASTGROUP PORT\n");
b5038e15
FT
24}
25
49697920 26static __attribute__ ((unused)) char *formataddress(struct sockaddr *arg, socklen_t arglen)
b5038e15
FT
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:
49697920 39 ret = sstrdup("Unix socket");
b5038e15
FT
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
60static int mkmcastsk4(struct in_addr group, int port)
61{
62 int fd;
aeddd223 63 int soval;
b5038e15
FT
64 struct sockaddr_in nm;
65 struct ip_mreqn mreq;
66
67 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
aeddd223
FT
68 soval = 1;
69 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &soval, sizeof(soval)))
70 return(-1);
b5038e15
FT
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;
aeddd223 78 if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)))
b5038e15 79 return(-1);
af3fc4a5
FT
80 soval = 1;
81 if(setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &soval, sizeof(soval)))
82 return(-1);
b5038e15
FT
83 return(fd);
84}
85
49697920 86static __attribute__ ((unused)) void test(int fd)
b5038e15
FT
87{
88 char buf[65536];
4f6c7408 89 int i, ret;
49697920 90 struct pollfd pfd;
b5038e15
FT
91
92 while(1) {
49697920
FT
93 pfd.fd = fd;
94 pfd.events = POLLIN;
95 ret = poll(&pfd, 1, -1);
b5038e15 96 if(ret < 0) {
49697920 97 fprintf(stderr, "mctap: poll: %s\n", strerror(errno));
b5038e15
FT
98 exit(1);
99 }
49697920
FT
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);
4f6c7408 112 }
b5038e15 113 }
3415540f
FT
114}
115
49697920
FT
116static 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);
1fefa40d 125 while(!quit) {
49697920
FT
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) {
a13b233b 133 syslog(LOG_ERR, "mctap: poll: %s", strerror(errno));
49697920
FT
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)) {
a13b233b 142 syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
49697920
FT
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));
3cb771e8 153 write(tap, buf, sizeof(pi) + ret);
49697920
FT
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)) {
a13b233b 161 syslog(LOG_ERR, "mctap: mcast packet: %s", strerror(errno));
49697920
FT
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
180static 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
1fefa40d
FT
195static 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
3415540f
FT
207int main(int argc, char **argv)
208{
209 int c;
49697920 210 int sock, tap;
b5038e15
FT
211 struct in_addr group;
212 int port;
49697920 213 char *tapname;
1fefa40d 214 char *pidfile;
a13b233b 215 int daemonize;
49697920 216 struct sockaddr_in dst;
1fefa40d 217 FILE *pidfd;
3415540f 218
49697920 219 tapname = "mctap";
a13b233b 220 daemonize = 0;
1fefa40d
FT
221 pidfile = NULL;
222 while((c = getopt(argc, argv, "hD:dpP:")) >= 0) {
3415540f 223 switch(c) {
49697920
FT
224 case 'D':
225 tapname = optarg;
226 break;
a13b233b
FT
227 case 'd':
228 daemonize = 1;
229 break;
1fefa40d
FT
230 case 'p':
231 pidfile = (void *)-1;
232 break;
233 case 'P':
234 pidfile = optarg;
235 break;
3415540f
FT
236 case 'h':
237 usage(stdout);
238 return(0);
239 default:
240 usage(stderr);
241 exit(1);
242 }
243 }
1fefa40d
FT
244 if(pidfile == (void *)-1)
245 pidfile = sprintf2("/var/run/mctap.%s.pid", tapname);
b5038e15
FT
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 }
49697920
FT
259 if((tap = maketap(tapname)) < 0) {
260 fprintf(stderr, "mctap: could not create TAP device: %s\n", strerror(errno));
261 exit(1);
262 }
a13b233b
FT
263 openlog(sprintf2("mctap-%s", tapname), LOG_PID, LOG_DAEMON);
264
1fefa40d
FT
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 }
a13b233b
FT
270 if(daemonize)
271 daemon(0, 0);
1fefa40d
FT
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);
3415540f 280
49697920
FT
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));
1fefa40d
FT
286 syslog(LOG_INFO, "exiting");
287
288 if(pidfile != NULL)
289 unlink(pidfile);
3415540f
FT
290 return(0);
291}