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