1/*
2 * Copyright (C) 2012 by Darren Reed.
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 */
6#if !defined(lint)
7static const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed";
8static 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]
40extern  char    *sys_errlist[];
41#else
42# define	STRERROR(x)     strerror(x)
43#endif
44
45
46int	main __P((int, char *[]));
47void	usage __P((char *));
48void	printsynchdr __P((synchdr_t *));
49void	printtable __P((int));
50void	printsmcproto __P((char *));
51void	printcommand __P((int));
52int	do_kbuff __P((int, char *, int *));
53int	do_packet __P((int, char *));
54int	buildsocket __P((char *, struct sockaddr_in *));
55void	do_io __P((void));
56void	handleterm __P((int));
57
58int	terminate = 0;
59int	igmpfd = -1;
60int	nfd = -1;
61int	lfd = -1;
62int	opts = 0;
63
64void
65usage(progname)
66	char *progname;
67{
68	fprintf(stderr,
69		"Usage: %s [-d] [-p port] [-i address] -I <interface>\n",
70		progname);
71}
72
73void
74handleterm(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
84int
85main(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#if BSD >= 199306
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();
197tryagain:
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
218void
219do_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
312int
313buildsocket(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			sprintf(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
451int
452do_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
513int
514do_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
599void
600printcommand(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
619void
620printtable(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
638void
639printsmcproto(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
664void
665printsynchdr(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