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