1/*	$NetBSD: ipfsyncd.c,v 1.1.1.2 2012/07/22 13:44:55 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)
9static const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed";
10static 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]
42extern  char    *sys_errlist[];
43#else
44# define	STRERROR(x)     strerror(x)
45#endif
46
47
48int	main __P((int, char *[]));
49void	usage __P((char *));
50void	printsynchdr __P((synchdr_t *));
51void	printtable __P((int));
52void	printsmcproto __P((char *));
53void	printcommand __P((int));
54int	do_kbuff __P((int, char *, int *));
55int	do_packet __P((int, char *));
56int	buildsocket __P((char *, struct sockaddr_in *));
57void	do_io __P((void));
58void	handleterm __P((int));
59
60int	terminate = 0;
61int	igmpfd = -1;
62int	nfd = -1;
63int	lfd = -1;
64int	opts = 0;
65
66void
67usage(progname)
68	char *progname;
69{
70	fprintf(stderr,
71		"Usage: %s [-d] [-p port] [-i address] -I <interface>\n",
72		progname);
73}
74
75void
76handleterm(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
86int
87main(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();
199tryagain:
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
220void
221do_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
314int
315buildsocket(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
453int
454do_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
515int
516do_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
601void
602printcommand(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
621void
622printtable(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
640void
641printsmcproto(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
666void
667printsynchdr(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