1/* $NetBSD: ipfsyncd.c,v 1.1.1.2 2012/07/22 13:44:55 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) 9static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; 10static 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] 42extern char *sys_errlist[]; 43#else 44# define STRERROR(x) strerror(x) 45#endif 46 47 48int main __P((int, char *[])); 49void usage __P((char *)); 50void printsynchdr __P((synchdr_t *)); 51void printtable __P((int)); 52void printsmcproto __P((char *)); 53void printcommand __P((int)); 54int do_kbuff __P((int, char *, int *)); 55int do_packet __P((int, char *)); 56int buildsocket __P((char *, struct sockaddr_in *)); 57void do_io __P((void)); 58void handleterm __P((int)); 59 60int terminate = 0; 61int igmpfd = -1; 62int nfd = -1; 63int lfd = -1; 64int opts = 0; 65 66void 67usage(progname) 68 char *progname; 69{ 70 fprintf(stderr, 71 "Usage: %s [-d] [-p port] [-i address] -I <interface>\n", 72 progname); 73} 74 75void 76handleterm(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 86int 87main(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(); 199tryagain: 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 220void 221do_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 314int 315buildsocket(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 453int 454do_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 515int 516do_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 601void 602printcommand(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 621void 622printtable(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 640void 641printsmcproto(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 666void 667printsynchdr(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