Home | History | Annotate | Line # | Download | only in tools
ipfsyncd.c revision 1.1.1.1.2.2
      1 /*	$NetBSD: ipfsyncd.c,v 1.1.1.1.2.2 2012/04/17 00:03:26 yamt Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2008 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.2.1 2012/01/26 05:44:26 darren_r 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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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