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