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]
36extern  char    *sys_errlist[];
37#else
38# define	STRERROR(x)     strerror(x)
39#endif
40
41
42int	main(int, char *[]);
43void	usage(char *);
44void	printsynchdr(synchdr_t *);
45void	printtable(int);
46void	printsmcproto(char *);
47void	printcommand(int);
48int	do_kbuff(int, char *, int *);
49int	do_packet(int, char *);
50int	buildsocket(char *, struct sockaddr_in *);
51void	do_io(void);
52void	handleterm(int);
53
54int	terminate = 0;
55int	igmpfd = -1;
56int	nfd = -1;
57int	lfd = -1;
58int	opts = 0;
59
60void
61usage(progname)
62	char *progname;
63{
64	fprintf(stderr,
65		"Usage: %s [-d] [-p port] [-i address] -I <interface>\n",
66		progname);
67}
68
69void
70handleterm(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
80int
81main(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();
193tryagain:
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
214void
215do_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
308int
309buildsocket(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
447int
448do_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
509int
510do_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
595void
596printcommand(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
615void
616printtable(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
634void
635printsmcproto(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
660void
661printsynchdr(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