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