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 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 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 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 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 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 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 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 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 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 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 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