xref: /freebsd/sbin/ipf/ipfsync/ipfsyncd.c (revision 4b9d6057)
1 /*
2  * Copyright (C) 2012 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 #include <sys/types.h>
7 #include <sys/time.h>
8 #include <sys/socket.h>
9 #include <sys/ioctl.h>
10 #include <sys/sockio.h>
11 #include <sys/errno.h>
12 
13 #include <netinet/in.h>
14 #include <net/if.h>
15 
16 #include <arpa/inet.h>
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <syslog.h>
24 #include <signal.h>
25 
26 #include "ipf.h"
27 #include "opts.h"
28 
29 
30 #define	R_IO_ERROR	-1
31 #define	R_OKAY		0
32 #define	R_MORE		1
33 #define	R_SKIP		2
34 #if	defined(sun) && !defined(SOLARIS2)
35 # define	STRERROR(x)     sys_errlist[x]
36 extern  char    *sys_errlist[];
37 #else
38 # define	STRERROR(x)     strerror(x)
39 #endif
40 
41 
42 int	main(int, char *[]);
43 void	usage(char *);
44 void	printsynchdr(synchdr_t *);
45 void	printtable(int);
46 void	printsmcproto(char *);
47 void	printcommand(int);
48 int	do_kbuff(int, char *, int *);
49 int	do_packet(int, char *);
50 int	buildsocket(char *, struct sockaddr_in *);
51 void	do_io(void);
52 void	handleterm(int);
53 
54 int	terminate = 0;
55 int	igmpfd = -1;
56 int	nfd = -1;
57 int	lfd = -1;
58 int	opts = 0;
59 
60 void
61 usage(progname)
62 	char *progname;
63 {
64 	fprintf(stderr,
65 		"Usage: %s [-d] [-p port] [-i address] -I <interface>\n",
66 		progname);
67 }
68 
69 void
70 handleterm(sig)
71 	int sig;
72 {
73 	terminate = sig;
74 }
75 
76 
77 /* should be large enough to hold header + any datatype */
78 #define BUFFERLEN 1400
79 
80 int
81 main(argc, argv)
82 	int argc;
83 	char *argv[];
84 {
85 	struct sockaddr_in sin;
86 	char *interface;
87 	char *progname;
88 	int opt, tries;
89 
90 	progname = strrchr(argv[0], '/');
91 	if (progname) {
92 		progname++;
93 	} else {
94 		progname = argv[0];
95 	}
96 
97 	opts = 0;
98 	tries = 0;
99 	interface = NULL;
100 
101 	bzero((char *)&sin, sizeof(sin));
102 	sin.sin_family = AF_INET;
103 	sin.sin_port = htons(0xaf6c);
104 	sin.sin_addr.s_addr = htonl(INADDR_UNSPEC_GROUP | 0x697066);
105 
106 	while ((opt = getopt(argc, argv, "di:I:p:")) != -1)
107 		switch (opt)
108 		{
109 		case 'd' :
110 			debuglevel++;
111 			break;
112 		case 'I' :
113 			interface = optarg;
114 			break;
115 		case 'i' :
116 			sin.sin_addr.s_addr = inet_addr(optarg);
117 			break;
118 		case 'p' :
119 			sin.sin_port = htons(atoi(optarg));
120 			break;
121 		}
122 
123 	if (interface == NULL) {
124 		usage(progname);
125 		exit(1);
126 	}
127 
128 	if (!debuglevel) {
129 
130 #ifdef BSD
131 		daemon(0, 0);
132 #else
133 		int fd = open("/dev/null", O_RDWR);
134 
135 		switch (fork())
136 		{
137 		case 0 :
138 			break;
139 
140 		case -1 :
141 			fprintf(stderr, "%s: fork() failed: %s\n",
142 				argv[0], STRERROR(errno));
143 			exit(1);
144 			/* NOTREACHED */
145 
146 		default :
147 			exit(0);
148 			/* NOTREACHED */
149 		}
150 
151 		dup2(fd, 0);
152 		dup2(fd, 1);
153 		dup2(fd, 2);
154 		close(fd);
155 
156 		setsid();
157 #endif
158 	}
159 
160        	signal(SIGHUP, handleterm);
161        	signal(SIGINT, handleterm);
162        	signal(SIGTERM, handleterm);
163 
164 	openlog(progname, LOG_PID, LOG_SECURITY);
165 
166 	while (!terminate) {
167 		if (lfd != -1) {
168 			close(lfd);
169 			lfd = -1;
170 		}
171 		if (nfd != -1) {
172 			close(nfd);
173 			nfd = -1;
174 		}
175 		if (igmpfd != -1) {
176 			close(igmpfd);
177 			igmpfd = -1;
178 		}
179 
180 		if (buildsocket(interface, &sin) == -1)
181 			goto tryagain;
182 
183 		lfd = open(IPSYNC_NAME, O_RDWR);
184 		if (lfd == -1) {
185 			syslog(LOG_ERR, "open(%s):%m", IPSYNC_NAME);
186 			debug(1, "open(%s): %s\n", IPSYNC_NAME,
187 			      STRERROR(errno));
188 			goto tryagain;
189 		}
190 
191 		tries = -1;
192 		do_io();
193 tryagain:
194 		tries++;
195 		syslog(LOG_INFO, "retry in %d seconds", 1 << tries);
196 		debug(1, "wait %d seconds\n", 1 << tries);
197 		sleep(1 << tries);
198 	}
199 
200 
201 	/* terminate */
202 	if (lfd != -1)
203 		close(lfd);
204 	if (nfd != -1)
205 		close(nfd);
206 
207 	syslog(LOG_ERR, "signal %d received, exiting...", terminate);
208 	debug(1, "signal %d received, exiting...", terminate);
209 
210 	exit(1);
211 }
212 
213 
214 void
215 do_io()
216 {
217 	char nbuff[BUFFERLEN];
218 	char buff[BUFFERLEN];
219 	fd_set mrd, rd;
220 	int maxfd;
221 	int inbuf;
222 	int n1;
223 	int left;
224 
225 	FD_ZERO(&mrd);
226 	FD_SET(lfd, &mrd);
227 	FD_SET(nfd, &mrd);
228 	maxfd = nfd;
229 	if (lfd > maxfd)
230 		maxfd = lfd;
231 	debug(2, "nfd %d lfd %d maxfd %d\n", nfd, lfd, maxfd);
232 
233 	inbuf = 0;
234 	/*
235 	 * A threaded approach to this loop would have one thread
236 	 * work on reading lfd (only) all the time and another thread
237 	 * working on reading nfd all the time.
238 	 */
239 	while (!terminate) {
240 		int n;
241 
242 		rd = mrd;
243 
244 		n = select(maxfd + 1, &rd, NULL, NULL, NULL);
245 		if (n < 0) {
246 			switch (errno)
247 			{
248 			case EINTR :
249 				continue;
250 			default :
251 				syslog(LOG_ERR, "select error: %m");
252 				debug(1, "select error: %s\n", STRERROR(errno));
253 				return;
254 			}
255 		}
256 
257 		if (FD_ISSET(lfd, &rd)) {
258 			n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf);
259 
260 			debug(3, "read(K):%d\n", n1);
261 
262 			if (n1 <= 0) {
263 				syslog(LOG_ERR, "read error (k-header): %m");
264 				debug(1, "read error (k-header): %s\n",
265 				      STRERROR(errno));
266 				return;
267 			}
268 
269 			left = 0;
270 
271 			switch (do_kbuff(n1, buff, &left))
272 			{
273 			case R_IO_ERROR :
274 				return;
275 			case R_MORE :
276 				inbuf += left;
277 				break;
278 			default :
279 				inbuf = 0;
280 				break;
281 			}
282 		}
283 
284 		if (FD_ISSET(nfd, &rd)) {
285 			n1 = recv(nfd, nbuff, sizeof(nbuff), 0);
286 
287 			debug(3, "read(N):%d\n", n1);
288 
289 			if (n1 <= 0) {
290 				syslog(LOG_ERR, "read error (n-header): %m");
291 				debug(1, "read error (n-header): %s\n",
292 				      STRERROR(errno));
293 				return;
294 			}
295 
296 			switch (do_packet(n1, nbuff))
297 			{
298 			case R_IO_ERROR :
299 				return;
300 			default :
301 				break;
302 			}
303 		}
304 	}
305 }
306 
307 
308 int
309 buildsocket(nicname, sinp)
310 	char *nicname;
311 	struct sockaddr_in *sinp;
312 {
313 	struct sockaddr_in *reqip;
314 	struct ifreq req;
315 	char opt;
316 
317 	debug(2, "binding to %s:%s\n", nicname, inet_ntoa(sinp->sin_addr));
318 
319 	if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
320 		struct in_addr addr;
321 		struct ip_mreq mreq;
322 
323 		igmpfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
324 		if (igmpfd == -1) {
325 			syslog(LOG_ERR, "socket:%m");
326 			debug(1, "socket:%s\n", STRERROR(errno));
327 			return -1;
328 		}
329 
330 		bzero((char *)&req, sizeof(req));
331 		strncpy(req.ifr_name, nicname, sizeof(req.ifr_name));
332 		req.ifr_name[sizeof(req.ifr_name) - 1] = '\0';
333 		if (ioctl(igmpfd, SIOCGIFADDR, &req) == -1) {
334 			syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m");
335 			debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno));
336 			close(igmpfd);
337 			igmpfd = -1;
338 			return -1;
339 		}
340 		reqip = (struct sockaddr_in *)&req.ifr_addr;
341 
342 		addr = reqip->sin_addr;
343 		if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_IF,
344 			       (char *)&addr, sizeof(addr)) == -1) {
345 			syslog(LOG_ERR, "setsockopt(IP_MULTICAST_IF(%s)):%m",
346 			       inet_ntoa(addr));
347 			debug(1, "setsockopt(IP_MULTICAST_IF(%s)):%s\n",
348 			      inet_ntoa(addr), STRERROR(errno));
349 			close(igmpfd);
350 			igmpfd = -1;
351 			return -1;
352 		}
353 
354 		opt = 0;
355 		if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_LOOP,
356 			       (char *)&opt, sizeof(opt)) == -1) {
357 			syslog(LOG_ERR, "setsockopt(IP_MULTICAST_LOOP=0):%m");
358 			debug(1, "setsockopt(IP_MULTICAST_LOOP=0):%s\n",
359 			      STRERROR(errno));
360 			close(igmpfd);
361 			igmpfd = -1;
362 			return -1;
363 		}
364 
365 		opt = 63;
366 		if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_TTL,
367 			       (char *)&opt, sizeof(opt)) == -1) {
368 			syslog(LOG_ERR, "setsockopt(IP_MULTICAST_TTL=%d):%m",
369 			       opt);
370 			debug(1, "setsockopt(IP_MULTICAST_TTL=%d):%s\n", opt,
371 			      STRERROR(errno));
372 			close(igmpfd);
373 			igmpfd = -1;
374 			return -1;
375 		}
376 
377 		mreq.imr_multiaddr.s_addr = sinp->sin_addr.s_addr;
378 		mreq.imr_interface.s_addr = reqip->sin_addr.s_addr;
379 
380 		if (setsockopt(igmpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
381 			       (char *)&mreq, sizeof(mreq)) == -1) {
382 			char buffer[80];
383 
384 			snprintf(buffer, sizeof(buffer), "%s,", inet_ntoa(sinp->sin_addr));
385 			strcat(buffer, inet_ntoa(reqip->sin_addr));
386 
387 			syslog(LOG_ERR,
388 			       "setsockpt(IP_ADD_MEMBERSHIP,%s):%m", buffer);
389 			debug(1, "setsockpt(IP_ADD_MEMBERSHIP,%s):%s\n",
390 			      buffer, STRERROR(errno));
391 			close(igmpfd);
392 			igmpfd = -1;
393 			return -1;
394 		}
395 	}
396 	nfd = socket(AF_INET, SOCK_DGRAM, 0);
397 	if (nfd == -1) {
398 		syslog(LOG_ERR, "socket:%m");
399 		if (igmpfd != -1) {
400 			close(igmpfd);
401 			igmpfd = -1;
402 		}
403 		return -1;
404 	}
405 	bzero((char *)&req, sizeof(req));
406 	strncpy(req.ifr_name, nicname, sizeof(req.ifr_name));
407 	req.ifr_name[sizeof(req.ifr_name) - 1] = '\0';
408 	if (ioctl(nfd, SIOCGIFADDR, &req) == -1) {
409 		syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m");
410 		debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno));
411 		close(igmpfd);
412 		igmpfd = -1;
413 		return -1;
414 	}
415 
416 	if (bind(nfd, (struct sockaddr *)&req.ifr_addr,
417 		 sizeof(req.ifr_addr)) == -1) {
418 		syslog(LOG_ERR, "bind:%m");
419 		debug(1, "bind:%s\n", STRERROR(errno));
420 		close(nfd);
421 		if (igmpfd != -1) {
422 			close(igmpfd);
423 			igmpfd = -1;
424 		}
425 		nfd = -1;
426 		return -1;
427 	}
428 
429 	if (connect(nfd, (struct sockaddr *)sinp, sizeof(*sinp)) == -1) {
430 		syslog(LOG_ERR, "connect:%m");
431 		debug(1, "connect:%s\n", STRERROR(errno));
432 		close(nfd);
433 		if (igmpfd != -1) {
434 			close(igmpfd);
435 			igmpfd = -1;
436 		}
437 		nfd = -1;
438 		return -1;
439 	}
440 	syslog(LOG_INFO, "Sending data to %s", inet_ntoa(sinp->sin_addr));
441 	debug(3, "Sending data to %s\n", inet_ntoa(sinp->sin_addr));
442 
443 	return nfd;
444 }
445 
446 
447 int
448 do_packet(pklen, buff)
449 	int pklen;
450 	char *buff;
451 {
452 	synchdr_t *sh;
453 	u_32_t magic;
454 	int len;
455 	int n2;
456 	int n3;
457 
458 	while (pklen > 0) {
459 		if (pklen < sizeof(*sh)) {
460 			syslog(LOG_ERR, "packet length too short:%d", pklen);
461 			debug(2, "packet length too short:%d\n", pklen);
462 			return R_SKIP;
463 		}
464 
465 		sh = (synchdr_t *)buff;
466 		len = ntohl(sh->sm_len);
467 		magic = ntohl(sh->sm_magic);
468 
469 		if (magic != SYNHDRMAGIC) {
470 			syslog(LOG_ERR, "invalid header magic %x", magic);
471 			debug(2, "invalid header magic %x\n", magic);
472 			return R_SKIP;
473 		}
474 
475 		if (pklen < len + sizeof(*sh)) {
476 			syslog(LOG_ERR, "packet length too short:%d", pklen);
477 			debug(2, "packet length too short:%d\n", pklen);
478 			return R_SKIP;
479 		}
480 
481 		if (debuglevel > 3) {
482 			printsynchdr(sh);
483 			printcommand(sh->sm_cmd);
484 			printtable(sh->sm_table);
485 			printsmcproto(buff);
486 		}
487 
488 		n2 = sizeof(*sh) + len;
489 
490 		do {
491 			n3 = write(lfd, buff, n2);
492 			if (n3 <= 0) {
493 				syslog(LOG_ERR, "write error: %m");
494 				debug(1, "write error: %s\n", STRERROR(errno));
495 				return R_IO_ERROR;
496 			}
497 
498 			n2 -= n3;
499 			buff += n3;
500 			pklen -= n3;
501 		} while (n3 != 0);
502 	}
503 
504 	return R_OKAY;
505 }
506 
507 
508 
509 int
510 do_kbuff(inbuf, buf, left)
511 	int inbuf, *left;
512 	char *buf;
513 {
514 	synchdr_t *sh;
515 	u_32_t magic;
516 	int complete;
517 	int sendlen;
518 	int error;
519 	int bytes;
520 	int len;
521 	int n2;
522 	int n3;
523 
524 	sendlen = 0;
525 	bytes = inbuf;
526 	error = R_OKAY;
527 	sh = (synchdr_t *)buf;
528 
529 	for (complete = 0; bytes > 0; complete++) {
530 		len = ntohl(sh->sm_len);
531 		magic = ntohl(sh->sm_magic);
532 
533 		if (magic != SYNHDRMAGIC) {
534 			syslog(LOG_ERR,
535 			       "read invalid header magic 0x%x, flushing",
536 			       magic);
537 			debug(2, "read invalid header magic 0x%x, flushing\n",
538 			       magic);
539 			n2 = SMC_RLOG;
540 			(void) ioctl(lfd, SIOCIPFFL, &n2);
541 			break;
542 		}
543 
544 		if (debuglevel > 3) {
545 			printsynchdr(sh);
546 			printcommand(sh->sm_cmd);
547 			printtable(sh->sm_table);
548 			putchar('\n');
549 		}
550 
551 		if (bytes < sizeof(*sh) + len) {
552 			debug(3, "Not enough bytes %d < %d\n", bytes,
553 			      sizeof(*sh) + len);
554 			error = R_MORE;
555 			break;
556 		}
557 
558 		if (debuglevel > 3) {
559 			printsmcproto(buf);
560 		}
561 
562 		sendlen += len + sizeof(*sh);
563 		sh = (synchdr_t *)(buf + sendlen);
564 		bytes -= sendlen;
565 	}
566 
567 	if (complete) {
568 		n3 = send(nfd, buf, sendlen, 0);
569 		if (n3 <= 0) {
570 			syslog(LOG_ERR, "write error: %m");
571 			debug(1, "write error: %s\n", STRERROR(errno));
572 			return R_IO_ERROR;
573 		}
574 		debug(3, "send on %d len %d = %d\n", nfd, sendlen, n3);
575 		error = R_OKAY;
576 	}
577 
578 	/* move buffer to the front,we might need to make
579 	 * this more efficient, by using a rolling pointer
580 	 * over the buffer and only copying it, when
581 	 * we are reaching the end
582 	 */
583 	if (bytes > 0) {
584 		bcopy(buf + bytes, buf, bytes);
585 		error = R_MORE;
586 	}
587 	debug(4, "complete %d bytes %d error %d\n", complete, bytes, error);
588 
589 	*left = bytes;
590 
591 	return error;
592 }
593 
594 
595 void
596 printcommand(cmd)
597 	int cmd;
598 {
599 
600 	switch (cmd)
601 	{
602 	case SMC_CREATE :
603 		printf(" cmd:CREATE");
604 		break;
605 	case SMC_UPDATE :
606 		printf(" cmd:UPDATE");
607 		break;
608 	default :
609 		printf(" cmd:Unknown(%d)", cmd);
610 		break;
611 	}
612 }
613 
614 
615 void
616 printtable(table)
617 	int table;
618 {
619 	switch (table)
620 	{
621 	case SMC_NAT :
622 		printf(" table:NAT");
623 		break;
624 	case SMC_STATE :
625 		printf(" table:STATE");
626 		break;
627 	default :
628 		printf(" table:Unknown(%d)", table);
629 		break;
630 	}
631 }
632 
633 
634 void
635 printsmcproto(buff)
636 	char *buff;
637 {
638 	syncupdent_t *su;
639 	synchdr_t *sh;
640 
641 	sh = (synchdr_t *)buff;
642 
643 	if (sh->sm_cmd == SMC_CREATE) {
644 		;
645 
646 	} else if (sh->sm_cmd == SMC_UPDATE) {
647 		su = (syncupdent_t *)buff;
648 		if (sh->sm_p == IPPROTO_TCP) {
649 			printf(" TCP Update: age %lu state %d/%d\n",
650 				su->sup_tcp.stu_age,
651 				su->sup_tcp.stu_state[0],
652 				su->sup_tcp.stu_state[1]);
653 		}
654 	} else {
655 		printf("Unknown command\n");
656 	}
657 }
658 
659 
660 void
661 printsynchdr(sh)
662 	synchdr_t *sh;
663 {
664 
665 	printf("v:%d p:%d num:%d len:%d magic:%x", sh->sm_v, sh->sm_p,
666 	       ntohl(sh->sm_num), ntohl(sh->sm_len), ntohl(sh->sm_magic));
667 }
668