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