├── .travis.yml ├── COPYING ├── GPL.txt ├── Makefile.am ├── NEWS ├── README ├── Stanford.txt ├── autogen.sh ├── configure.ac ├── doc ├── Makefile.am ├── igmpproxy.8.in └── igmpproxy.conf.5.in ├── igmpproxy.conf └── src ├── Makefile.am ├── callout.c ├── config.c ├── confread.c ├── ifvc.c ├── igmp.c ├── igmpproxy.c ├── igmpproxy.h ├── igmpv3.h ├── kern.c ├── lib.c ├── mroute-api.c ├── os-dragonfly.h ├── os-freebsd.h ├── os-linux.h ├── os-netbsd.h ├── os-openbsd.h ├── os-qnxnto.h ├── request.c ├── rttable.c └── syslog.c /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | matrix: 4 | include: 5 | - compiler: gcc-4.4 6 | dist: trusty 7 | env: PLATFORM=x86 8 | addons: 9 | apt: 10 | packages: 11 | - libc6-dev:i386 12 | - gcc-4.4-multilib 13 | - compiler: gcc-4.4 14 | dist: trusty 15 | env: PLATFORM=x86_64 16 | addons: 17 | apt: 18 | packages: 19 | - gcc-4.4 20 | - compiler: gcc-4.6 21 | dist: trusty 22 | env: PLATFORM=x86 23 | addons: 24 | apt: 25 | packages: 26 | - libc6-dev:i386 27 | - gcc-4.6-multilib 28 | - compiler: gcc-4.6 29 | dist: trusty 30 | env: PLATFORM=x86_64 31 | addons: 32 | apt: 33 | packages: 34 | - gcc-4.6 35 | - compiler: gcc-4.7 36 | env: PLATFORM=x86 37 | addons: 38 | apt: 39 | packages: 40 | - libc6-dev:i386 41 | - gcc-4.7-multilib 42 | - compiler: gcc-4.7 43 | env: PLATFORM=x86_64 44 | addons: 45 | apt: 46 | packages: 47 | - gcc-4.7 48 | - compiler: gcc-4.8 49 | env: PLATFORM=x86 50 | addons: 51 | apt: 52 | packages: 53 | - libc6-dev:i386 54 | - gcc-4.8-multilib 55 | - compiler: gcc-4.8 56 | env: PLATFORM=x86_64 57 | addons: 58 | apt: 59 | packages: 60 | - gcc-4.8 61 | - compiler: gcc-4.9 62 | env: PLATFORM=x86 63 | addons: 64 | apt: 65 | packages: 66 | - libc6-dev:i386 67 | - gcc-4.9-multilib 68 | - compiler: gcc-4.9 69 | env: PLATFORM=x86_64 70 | addons: 71 | apt: 72 | packages: 73 | - gcc-4.9 74 | - compiler: gcc-5 75 | env: PLATFORM=x86 76 | addons: 77 | apt: 78 | packages: 79 | - libc6-dev:i386 80 | - gcc-5-multilib 81 | - compiler: gcc-5 82 | env: PLATFORM=x86_64 83 | addons: 84 | apt: 85 | packages: 86 | - gcc-5 87 | - compiler: gcc-6 88 | env: PLATFORM=x86 89 | addons: 90 | apt: 91 | sources: 92 | - ubuntu-toolchain-r-test 93 | packages: 94 | - libc6-dev:i386 95 | - gcc-6-multilib 96 | - compiler: gcc-6 97 | env: PLATFORM=x86_64 98 | addons: 99 | apt: 100 | sources: 101 | - ubuntu-toolchain-r-test 102 | packages: 103 | - gcc-6 104 | - compiler: gcc-7 105 | dist: trusty 106 | env: PLATFORM=x86 107 | addons: 108 | apt: 109 | sources: 110 | - ubuntu-toolchain-r-test 111 | packages: 112 | - libc6-dev:i386 113 | - gcc-7-multilib 114 | - compiler: gcc-7 115 | dist: trusty 116 | env: PLATFORM=x86_64 117 | addons: 118 | apt: 119 | sources: 120 | - ubuntu-toolchain-r-test 121 | packages: 122 | - gcc-7 123 | - compiler: gcc-8 124 | dist: trusty 125 | env: PLATFORM=x86 126 | addons: 127 | apt: 128 | sources: 129 | - ubuntu-toolchain-r-test 130 | packages: 131 | - libc6-dev:i386 132 | - gcc-8-multilib 133 | - compiler: gcc-8 134 | dist: trusty 135 | env: PLATFORM=x86_64 136 | addons: 137 | apt: 138 | sources: 139 | - ubuntu-toolchain-r-test 140 | packages: 141 | - gcc-8 142 | - compiler: gcc-9 143 | dist: trusty 144 | env: PLATFORM=x86 145 | addons: 146 | apt: 147 | sources: 148 | - ubuntu-toolchain-r-test 149 | packages: 150 | - libc6-dev:i386 151 | - gcc-9-multilib 152 | - compiler: gcc-9 153 | dist: trusty 154 | env: PLATFORM=x86_64 155 | addons: 156 | apt: 157 | sources: 158 | - ubuntu-toolchain-r-test 159 | packages: 160 | - gcc-9 161 | - compiler: clang-3.3 162 | dist: trusty 163 | env: PLATFORM=x86 164 | addons: 165 | apt: 166 | packages: 167 | - libc6-dev:i386 168 | - clang-3.3 169 | - gcc-multilib 170 | - compiler: clang-3.3 171 | dist: trusty 172 | env: PLATFORM=x86_64 173 | addons: 174 | apt: 175 | packages: 176 | - clang-3.3 177 | - compiler: clang-3.4 178 | dist: trusty 179 | env: PLATFORM=x86 180 | addons: 181 | apt: 182 | packages: 183 | - libc6-dev:i386 184 | - clang-3.4 185 | - gcc-multilib 186 | - compiler: clang-3.4 187 | dist: trusty 188 | env: PLATFORM=x86_64 189 | addons: 190 | apt: 191 | packages: 192 | - clang-3.4 193 | - compiler: clang-3.5 194 | env: PLATFORM=x86 195 | addons: 196 | apt: 197 | packages: 198 | - libc6-dev:i386 199 | - clang-3.5 200 | - gcc-multilib 201 | - compiler: clang-3.5 202 | env: PLATFORM=x86_64 203 | addons: 204 | apt: 205 | packages: 206 | - clang-3.5 207 | - compiler: clang-3.6 208 | env: PLATFORM=x86 209 | addons: 210 | apt: 211 | packages: 212 | - libc6-dev:i386 213 | - clang-3.6 214 | - gcc-multilib 215 | - compiler: clang-3.6 216 | env: PLATFORM=x86_64 217 | addons: 218 | apt: 219 | packages: 220 | - clang-3.6 221 | - compiler: clang-3.7 222 | env: PLATFORM=x86 223 | addons: 224 | apt: 225 | packages: 226 | - libc6-dev:i386 227 | - clang-3.7 228 | - gcc-multilib 229 | - compiler: clang-3.7 230 | env: PLATFORM=x86_64 231 | addons: 232 | apt: 233 | packages: 234 | - clang-3.7 235 | - compiler: clang-3.8 236 | env: PLATFORM=x86 237 | addons: 238 | apt: 239 | packages: 240 | - libc6-dev:i386 241 | - clang-3.8 242 | - gcc-multilib 243 | - compiler: clang-3.8 244 | env: PLATFORM=x86_64 245 | addons: 246 | apt: 247 | packages: 248 | - clang-3.8 249 | - compiler: clang-3.9 250 | env: PLATFORM=x86 251 | addons: 252 | apt: 253 | packages: 254 | - libc6-dev:i386 255 | - clang-3.9 256 | - gcc-multilib 257 | - compiler: clang-3.9 258 | env: PLATFORM=x86_64 259 | addons: 260 | apt: 261 | packages: 262 | - clang-3.9 263 | - compiler: clang-4.0 264 | env: PLATFORM=x86 265 | addons: 266 | apt: 267 | packages: 268 | - libc6-dev:i386 269 | - clang-4.0 270 | - gcc-multilib 271 | - compiler: clang-4.0 272 | env: PLATFORM=x86_64 273 | addons: 274 | apt: 275 | packages: 276 | - clang-4.0 277 | - compiler: clang-5.0 278 | env: PLATFORM=x86 279 | addons: 280 | apt: 281 | packages: 282 | - libc6-dev:i386 283 | - clang-5.0 284 | - gcc-multilib 285 | - compiler: clang-5.0 286 | env: PLATFORM=x86_64 287 | addons: 288 | apt: 289 | packages: 290 | - clang-5.0 291 | - compiler: clang-6.0 292 | env: PLATFORM=x86 293 | addons: 294 | apt: 295 | packages: 296 | - libc6-dev:i386 297 | - clang-6.0 298 | - gcc-multilib 299 | - compiler: clang-6.0 300 | env: PLATFORM=x86_64 301 | addons: 302 | apt: 303 | packages: 304 | - clang-6.0 305 | - compiler: clang-7 306 | dist: xenial 307 | env: PLATFORM=x86 308 | addons: 309 | apt: 310 | sources: 311 | - llvm-toolchain-xenial-7 312 | packages: 313 | - libc6-dev:i386 314 | - clang-7 315 | - gcc-multilib 316 | - compiler: clang-7 317 | dist: xenial 318 | env: PLATFORM=x86_64 319 | addons: 320 | apt: 321 | sources: 322 | - llvm-toolchain-xenial-7 323 | packages: 324 | - clang-7 325 | - compiler: clang-8 326 | dist: xenial 327 | env: PLATFORM=x86 328 | addons: 329 | apt: 330 | sources: 331 | - llvm-toolchain-xenial-8 332 | packages: 333 | - libc6-dev:i386 334 | - clang-8 335 | - gcc-multilib 336 | - compiler: clang-8 337 | dist: xenial 338 | env: PLATFORM=x86_64 339 | addons: 340 | apt: 341 | sources: 342 | - llvm-toolchain-xenial-8 343 | packages: 344 | - clang-8 345 | - compiler: clang-9 346 | dist: xenial 347 | env: PLATFORM=x86 348 | addons: 349 | apt: 350 | sources: 351 | - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' 352 | key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' 353 | packages: 354 | - libc6-dev:i386 355 | - clang-9 356 | - gcc-multilib 357 | - compiler: clang-9 358 | dist: xenial 359 | env: PLATFORM=x86_64 360 | addons: 361 | apt: 362 | sources: 363 | - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' 364 | key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' 365 | packages: 366 | - clang-9 367 | - compiler: gcc 368 | dist: precise 369 | env: PLATFORM=x86 370 | DIST=precise 371 | addons: 372 | apt: 373 | sources: 374 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 375 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 376 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 377 | packages: 378 | - libc6-dev:i386 379 | - gcc-multilib 380 | - compiler: gcc 381 | dist: precise 382 | env: PLATFORM=x86_64 383 | DIST=precise 384 | - compiler: gcc 385 | dist: trusty 386 | env: PLATFORM=x86 387 | DIST=trusty 388 | addons: 389 | apt: 390 | sources: 391 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 392 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 393 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 394 | packages: 395 | - libc6-dev:i386 396 | - gcc-multilib 397 | - compiler: gcc 398 | dist: trusty 399 | env: PLATFORM=x86_64 400 | DIST=trusty 401 | - compiler: gcc 402 | dist: xenial 403 | env: PLATFORM=x86 404 | DIST=xenial 405 | addons: 406 | apt: 407 | packages: 408 | - libc6-dev:i386 409 | - gcc-multilib 410 | - compiler: gcc 411 | dist: xenial 412 | env: PLATFORM=x86_64 413 | DIST=xenial 414 | - compiler: gcc 415 | dist: bionic 416 | env: PLATFORM=x86 417 | DIST=bionic 418 | addons: 419 | apt: 420 | packages: 421 | - libc6-dev:i386 422 | - gcc-multilib 423 | - compiler: gcc 424 | dist: bionic 425 | env: PLATFORM=x86_64 426 | DIST=bionic 427 | - compiler: gcc 428 | dist: precise 429 | env: PLATFORM=x86 430 | DIST=precise 431 | NOOPT=1 432 | addons: 433 | apt: 434 | sources: 435 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 436 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 437 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 438 | packages: 439 | - libc6-dev:i386 440 | - gcc-multilib 441 | - compiler: gcc 442 | dist: precise 443 | env: PLATFORM=x86_64 444 | DIST=precise 445 | NOOPT=1 446 | - compiler: gcc 447 | dist: trusty 448 | env: PLATFORM=x86 449 | DIST=trusty 450 | NOOPT=1 451 | addons: 452 | apt: 453 | packages: 454 | - libc6-dev:i386 455 | - gcc-multilib 456 | - compiler: gcc 457 | dist: trusty 458 | env: PLATFORM=x86_64 459 | DIST=trusty 460 | NOOPT=1 461 | - compiler: gcc 462 | dist: xenial 463 | env: PLATFORM=x86 464 | DIST=xenial 465 | NOOPT=1 466 | addons: 467 | apt: 468 | packages: 469 | - libc6-dev:i386 470 | - gcc-multilib 471 | - compiler: gcc 472 | dist: xenial 473 | env: PLATFORM=x86_64 474 | DIST=xenial 475 | NOOPT=1 476 | - compiler: gcc 477 | dist: bionic 478 | env: PLATFORM=x86 479 | DIST=bionic 480 | NOOPT=1 481 | addons: 482 | apt: 483 | packages: 484 | - libc6-dev:i386 485 | - gcc-multilib 486 | - compiler: gcc 487 | dist: bionic 488 | env: PLATFORM=x86_64 489 | DIST=bionic 490 | NOOPT=1 491 | - compiler: clang 492 | dist: precise 493 | env: PLATFORM=x86 494 | DIST=precise 495 | addons: 496 | apt: 497 | sources: 498 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 499 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 500 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 501 | packages: 502 | - libc6-dev:i386 503 | - gcc-multilib 504 | - compiler: clang 505 | dist: precise 506 | env: PLATFORM=x86_64 507 | DIST=precise 508 | - compiler: clang 509 | dist: trusty 510 | env: PLATFORM=x86 511 | DIST=trusty 512 | addons: 513 | apt: 514 | packages: 515 | - libc6-dev:i386 516 | - gcc-multilib 517 | - compiler: clang 518 | dist: trusty 519 | env: PLATFORM=x86_64 520 | DIST=trusty 521 | - compiler: clang 522 | dist: xenial 523 | env: PLATFORM=x86 524 | DIST=xenial 525 | addons: 526 | apt: 527 | packages: 528 | - libc6-dev:i386 529 | - gcc-multilib 530 | - compiler: clang 531 | dist: xenial 532 | env: PLATFORM=x86_64 533 | DIST=xenial 534 | - compiler: clang 535 | dist: bionic 536 | env: PLATFORM=x86 537 | DIST=bionic 538 | addons: 539 | apt: 540 | packages: 541 | - libc6-dev:i386 542 | - gcc-multilib 543 | - compiler: clang 544 | dist: bionic 545 | env: PLATFORM=x86_64 546 | DIST=bionic 547 | - compiler: clang 548 | dist: precise 549 | env: PLATFORM=x86 550 | DIST=precise 551 | NOOPT=1 552 | addons: 553 | apt: 554 | sources: 555 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 556 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 557 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 558 | packages: 559 | - libc6-dev:i386 560 | - gcc-multilib 561 | - compiler: clang 562 | dist: precise 563 | env: PLATFORM=x86_64 564 | DIST=precise 565 | NOOPT=1 566 | - compiler: clang 567 | dist: trusty 568 | env: PLATFORM=x86 569 | DIST=trusty 570 | NOOPT=1 571 | addons: 572 | apt: 573 | packages: 574 | - libc6-dev:i386 575 | - gcc-multilib 576 | - compiler: clang 577 | dist: trusty 578 | env: PLATFORM=x86_64 579 | DIST=trusty 580 | NOOPT=1 581 | - compiler: clang 582 | dist: xenial 583 | env: PLATFORM=x86 584 | DIST=xenial 585 | NOOPT=1 586 | addons: 587 | apt: 588 | packages: 589 | - libc6-dev:i386 590 | - gcc-multilib 591 | - compiler: clang 592 | dist: xenial 593 | env: PLATFORM=x86_64 594 | DIST=xenial 595 | NOOPT=1 596 | - compiler: clang 597 | dist: bionic 598 | env: PLATFORM=x86 599 | DIST=bionic 600 | NOOPT=1 601 | addons: 602 | apt: 603 | packages: 604 | - libc6-dev:i386 605 | - gcc-multilib 606 | - compiler: clang 607 | dist: bionic 608 | env: PLATFORM=x86_64 609 | DIST=bionic 610 | NOOPT=1 611 | - compiler: tcc 612 | dist: precise 613 | env: PLATFORM=x86_64 614 | DIST=precise 615 | addons: 616 | apt: 617 | sources: 618 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise main restricted universe multiverse' 619 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse' 620 | - sourceline: 'deb http://old-releases.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse' 621 | packages: 622 | - tcc 623 | - compiler: tcc 624 | dist: trusty 625 | env: PLATFORM=x86_64 626 | DIST=trusty 627 | addons: 628 | apt: 629 | packages: 630 | - tcc 631 | - compiler: tcc 632 | dist: xenial 633 | env: PLATFORM=x86_64 634 | DIST=xenial 635 | addons: 636 | apt: 637 | packages: 638 | - tcc 639 | - compiler: tcc 640 | dist: bionic 641 | env: PLATFORM=x86_64 642 | DIST=bionic 643 | addons: 644 | apt: 645 | packages: 646 | - tcc 647 | - compiler: gcc 648 | dist: trusty 649 | env: PLATFORM=x32 650 | addons: 651 | apt: 652 | packages: 653 | - libc6-dev-x32 654 | - gcc-multilib 655 | - compiler: powerpc-linux-gnu-gcc 656 | dist: trusty 657 | env: PLATFORM=powerpc 658 | addons: 659 | apt: 660 | packages: 661 | - gcc-powerpc-linux-gnu 662 | - libc6-dev-powerpc-cross 663 | - qemu-user 664 | - compiler: arm-linux-gnueabi-gcc 665 | dist: trusty 666 | env: PLATFORM=arm 667 | addons: 668 | apt: 669 | packages: 670 | - gcc-arm-linux-gnueabi 671 | - libc6-dev-armel-cross 672 | - qemu-user 673 | 674 | before_script: 675 | - if ! which "$CC" &>/dev/null; then export CC=${CC%%-*}; fi 676 | - export CFLAGS="-W -Wall -Werror -g" 677 | - if [ -z "$NOOPT" ]; then export CFLAGS="$CFLAGS -O2"; fi 678 | - if [[ "$CC" =~ "gcc" && "$CC" != "gcc-4.4" ]]; then export CFLAGS="$CFLAGS -Wno-error=unused-but-set-variable"; fi # needed for AC_PROG_CC_C99 679 | - case "$PLATFORM" in 680 | "x86") export CFLAGS="-m32 $CFLAGS" ;; 681 | "x86_64"|"") ;; 682 | "x32") export CFLAGS="-mx32 $CFLAGS"; export LDFLAGS="--static" ;; 683 | "powerpc") export CONFIGURE_FLAGS="--host=powerpc-linux-gnu"; export LDFLAGS="--static" ;; 684 | "arm") export CONFIGURE_FLAGS="--host=arm-linux-gnueabi"; export LDFLAGS="--static" ;; 685 | *) echo "Unsupported platform '$PLATFORM'"; exit 1 ;; 686 | esac 687 | 688 | script: 689 | - ./autogen.sh 690 | - ./configure $CONFIGURE_FLAGS 691 | - make 692 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | igmpproxy - IGMP proxy based multicast router 2 | Copyright (C) 2005 Johnny Egeland 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | 19 | This software is derived work from the following software. The original 20 | source code has been modified from it's original state by the author 21 | of igmpproxy. 22 | 23 | smcroute 0.92 - Copyright (C) 2001 Carsten Schill 24 | - Licensed under the GNU General Public License, either version 2 or 25 | any later version. 26 | 27 | mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 28 | Leland Stanford Junior University. 29 | - Licensed under the 3-clause BSD license, see Stanford.txt file. 30 | 31 | 32 | Since 2017-03-25 igmpproxy is GPLv2+ compatible, mrouted licensed 33 | was switched from the proprietary Stanford to 3-clause BSD. New 34 | igmpproxy contributions and patches must be under GPLv2+ license, 35 | old proprietary Stanford is not accepted. 36 | -------------------------------------------------------------------------------- /GPL.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = doc src 2 | EXTRA_DIST = autogen.sh GPL.txt Stanford.txt 3 | dist_sysconf_DATA = igmpproxy.conf 4 | 5 | .PHONY: ChangeLog 6 | ChangeLog: 7 | @if git -C $(srcdir) log --format="" -1 > /dev/null; then \ 8 | echo "Generating $@"; \ 9 | if test -f $(srcdir)/$@; then chmod u+w $(srcdir)/$@; fi; \ 10 | git -C $(srcdir) log --format="%ai %aN <%aE>%n%n%x09* %B" | sed 's/^\([^2\t]\)/\t \1/' > $(srcdir)/$@; \ 11 | elif ! test -f $(srcdir)/$@; then \ 12 | echo "Cannot generate ChangeLog: Not in git repository"; \ 13 | exit 1; \ 14 | fi 15 | 16 | AUTHORS: $(srcdir)/ChangeLog 17 | @echo "Generating $@" 18 | @if test -f $(srcdir)/$@; then chmod u+w $(srcdir)/$@; fi 19 | @{ \ 20 | echo "Authors and contributors, in alphabetical order:"; echo; \ 21 | sed -r "s/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [+-][0-9]{4} (.*)/\1/;t;d" $< | LC_ALL=C sort -u; \ 22 | } > $(srcdir)/$@ 23 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | New releases, the Git repository and the bug tracker are accessible 2 | at project homepage: 3 | 4 | https://github.com/pali/igmpproxy 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | IGMPproxy is a simple mulitcast router that only uses the IGMP protocol. 2 | 3 | Supported operating systems: 4 | - Linux 5 | - FreeBSD 6 | - NetBSD 7 | - OpenBSD 8 | - DragonFly BSD 9 | 10 | This software is released under the GNU GPL license v2 or later. See details in COPYING. 11 | -------------------------------------------------------------------------------- /Stanford.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2002 The Board of Trustees of the Leland Stanford Junior 2 | University 3 | Permission is hereby granted to STANFORD's rights, free of charge, to any 4 | person obtaining a copy of this Software and associated documentation files 5 | ( "MROUTED"), to deal in MROUTED without restriction, including without 6 | limitation the rights to use, copy, modify, merge, publish, distribute, 7 | sublicense, and/or sell copies of MROUTED , and to permit persons to whom 8 | MROUTED is furnished to do so, subject to the following conditions: 9 | 1) The above copyright notice and this permission notice shall be 10 | included in all copies or substantial portions of the MROUTED . 11 | 2) Neither the STANFORD name nor the names of its contributors may be 12 | used in any promotional advertising or other promotional materials to be 13 | disseminated to the public or any portion thereof nor to use the name of 14 | any STANFORD faculty member, employee, or student, or any trademark, 15 | service mark, trade name, or symbol of STANFORD or Stanford Hospitals and 16 | Clinics, nor any that is associated with any of them, without STANFORD's 17 | prior written consent. Any use of STANFORD's name shall be limited to 18 | statements of fact and shall not imply endorsement of any products or 19 | services. 20 | 21 | 3) MROUTED IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | FROM, OUT OF OR IN CONNECTION WITH MROUTED OR THE USE OR OTHER DEALINGS IN 27 | THE MROUTED . 28 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | aclocal 4 | autoconf 5 | autoheader 6 | automake --add-missing 7 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.63]) 2 | AC_INIT([igmpproxy], [0.4]) 3 | AM_INIT_AUTOMAKE 4 | AC_CONFIG_SRCDIR([src/igmpproxy.c]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | AC_PROG_CC_C99 7 | 8 | AS_IF([test "$ac_cv_prog_cc_c99" = "no"], [AC_MSG_ERROR([Your C compiler does not support ISO C99.])]) 9 | 10 | AC_CANONICAL_HOST 11 | case $host_os in 12 | linux*|uclinux*) os=linux;; 13 | freebsd*) os=freebsd;; 14 | netbsd*) os=netbsd;; 15 | openbsd*) os=openbsd;; 16 | nto*) os=qnxnto;; 17 | dragonfly*) os=dragonfly;; 18 | *) AC_MSG_ERROR([OS $host_os is not supported]);; 19 | esac 20 | AC_CONFIG_LINKS([src/os.h:src/os-${os}.h]) 21 | 22 | AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [[ 23 | #include 24 | #include 25 | ]]) 26 | AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], [], [], [[ 27 | #include 28 | #include 29 | ]]) 30 | 31 | # Check for Linux-style extension struct ip_mreqn (Linux, FreeBSD) 32 | # Adopted from https://github.com/troglobit/smcroute/blob/2.5.6/configure.ac#L144 33 | AC_CHECK_MEMBER([struct ip_mreqn.imr_ifindex], 34 | AC_DEFINE([HAVE_STRUCT_IP_MREQN], [1], [Define to 1 if you have a Linux-style struct ip_mreqn]), 35 | [], [[#include ]]) 36 | 37 | AC_SEARCH_LIBS(socket, socket) 38 | 39 | AC_SEARCH_LIBS([clock_gettime],[rt]) 40 | 41 | AC_CONFIG_FILES([ 42 | Makefile 43 | doc/Makefile 44 | src/Makefile 45 | doc/igmpproxy.8 46 | doc/igmpproxy.conf.5 47 | ]) 48 | AC_OUTPUT 49 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = igmpproxy.8 igmpproxy.conf.5 2 | -------------------------------------------------------------------------------- /doc/igmpproxy.8.in: -------------------------------------------------------------------------------- 1 | .\" .br - line break (nothing else on the line) 2 | .\" .B - bold 3 | .\" .I - green or kursive (on HTML) 4 | .\" .TP - paragraph ? (header line, followed by indented lines) 5 | .\" 6 | .TH igmpproxy 8 "" "@PACKAGE_STRING@" 7 | .SH NAME 8 | igmpproxy \- Multicast router utilizing IGMP forwarding 9 | 10 | .SH SYNOPSIS 11 | .B igmpproxy [-h] [-n] [-d] [-v [-v]] 12 | .I config-file 13 | 14 | 15 | .SH DESCRIPTION 16 | .B igmpproxy 17 | is a simple multicast routing daemon which uses IGMP forwarding to 18 | dynamically route multicast traffic. Routing is done by defining an 19 | "upstream" interface on which the daemon act as a normal Multicast 20 | client, and one or more "downstream" interfaces that serves clients 21 | on the destination networks. This is useful in situations where other 22 | dynamic multicast routers (like 'mrouted' or 'pimd') cannot be used. 23 | 24 | Since 25 | .B igmpproxy 26 | only uses IGMP signalling, the daemon is only suited for situations 27 | where multicast traffic comes from only one neighbouring network. 28 | In more advanced cases, 'mrouted' or 'pimd' is probably more suited. 29 | The daemon is not designed for cascading, and probably won't scale 30 | very well. 31 | 32 | Currently only IGMPv1 and v2 is supported on downstream interfaces. 33 | On the upstream interface the kernel IGMP client implementation is used, 34 | and supported IGMP versions is therefore limited to that supported by the 35 | kernel. 36 | 37 | 38 | .SH OPTIONS 39 | .IP -h 40 | Display help. 41 | .IP -v 42 | Verbose logging. Set logging level to INFO instead of WARNING used by default. 43 | .IP -vv 44 | More verbose logging. Set logging level to DEBUG. 45 | .IP -n 46 | Do not run as a daemon. 47 | .IP -d 48 | Output log messages to STDERR instead of to 49 | .BR syslog (3). 50 | Implies \fB\-n\fP. 51 | 52 | 53 | .SH LIMITS 54 | The current version compiles and runs fine with the Linux kernel version 2.4. The known limits are: 55 | 56 | .B Multicast routes: 57 | more then 200 58 | 59 | .B Multicast group membership: 60 | max. 20 61 | .SH FILES 62 | .TP 63 | .B /proc/net/ip_mr_cache 64 | - contains the active multicast routes 65 | .TP 66 | .B /proc/net/ip_mr_vif 67 | - contains the 'virtual' interfaces used by the active multicast routing daemon 68 | .TP 69 | .B /proc/sys/net/ipv4/conf//force_igmp_version 70 | - can be set to control what IGMP version the kernel should use on the upstream interface. 71 | Ex.: 'echo 2 > /proc/sys/net/ipv4/conf/eth0/force_igmp_version' will force the kernel to 72 | use IGMPv2 on eth0 (provided this is the upstream interface). 73 | 74 | 75 | .SH SEE ALSO 76 | .BR igmpproxy.conf (5), 77 | .BR mrouted, 78 | .BR pimd, 79 | .BR smcroute 80 | 81 | .SH BUGS 82 | Currently none (but there probably will be :-/ ) 83 | .SH AUTHOR 84 | Originally written by Johnny Egeland . 85 | -------------------------------------------------------------------------------- /doc/igmpproxy.conf.5.in: -------------------------------------------------------------------------------- 1 | .\" .br - line break (nothing else on the line) 2 | .\" .B - bold 3 | .\" .I - green or kursive (on HTML) 4 | .\" .TP - paragraph ? (header line, followed by indented lines) 5 | .\" 6 | .TH igmpproxy.conf 5 "" "@PACKAGE_STRING@" 7 | .SH NAME 8 | igmpproxy.conf \- Configuration file for 9 | .BR igmpproxy (8) 10 | multicast daemon 11 | 12 | .SH DESCRIPTION 13 | .B igmpproxy.conf 14 | contains the configuration for the 15 | .B igmpproxy 16 | multicast daemon. It defines which network interfaces should be 17 | used by the routing daemon. Each interface must be give one of the following roles: 18 | .B upstream 19 | , 20 | .B downstream 21 | or 22 | .B disabled 23 | . 24 | 25 | The 26 | .B upstream 27 | network interface is the outgoing interface which is responsible for communicating 28 | to available multicast data sources. There can only be one upstream interface. 29 | 30 | .B Downstream 31 | network interfaces are the distribution interfaces to the destination networks, 32 | where multicast clients can join groups and receive multicast data. One or more 33 | downstream interfaces must be configured. 34 | 35 | On 36 | .B disabled 37 | network interfaces all IGMP or multicast traffic is ignored altogether. If multiple 38 | IP addresses is used on one single interface (ae. eth0:1 ...), all interface 39 | aliases not in use should be configured as disabled. 40 | 41 | Any line in the configuration file starting with 42 | .B # 43 | is treated as a comment. Keywords and parameters can be distributed over many lines. 44 | The configuration file has four main keywords: 45 | 46 | .B chroot 47 | .I directory 48 | .RS 49 | Changes the apparent root directory to the given path after startup, thus 50 | denying 51 | .B igmpproxy 52 | access to files and commands outside that environmental directory tree. 53 | .RE 54 | 55 | 56 | .B user 57 | .I username 58 | .RS 59 | Specifies the userid to which 60 | .B igmpproxy 61 | will change after startup. 62 | .B igmpproxy 63 | must be started as root, but it will drop root privileges to the specified 64 | user. 65 | .RE 66 | 67 | 68 | .B quickleave 69 | .RS 70 | Enables quickleave mode. In this mode the daemon will send a Leave IGMP message 71 | upstream as soon as it receives a Leave message for any downstream interface. 72 | The daemon will then ask for Membership reports on the downstream interfaces, 73 | and if a report is received the group is joined again upstream. Normally this 74 | is not noticed at all by clients on the downstream networks. If it's vital 75 | that the daemon should act exactly as a real multicast client on the upstream 76 | interface, this function should not be used. Disabling this function increases 77 | the risk of bandwidth saturation. 78 | .RE 79 | 80 | 81 | .B phyint 82 | .I interface 83 | .I role 84 | [ ratelimit 85 | .I limit 86 | ] [ threshold 87 | .I ttl 88 | ] [ altnet 89 | .I networkaddr ... 90 | ] 91 | .RS 92 | Defines the state and settings of a network interface. 93 | .RE 94 | 95 | .SH PHYINT OPTIONS 96 | 97 | .B interface 98 | .RS 99 | The name of the interface the settings are for. This option is required for 100 | phyint settings. 101 | .RE 102 | 103 | .B role 104 | .RS 105 | The role of the interface. This should be either 106 | .B upstream 107 | (only one interface), 108 | .B downstream 109 | (one or more interfaces) or 110 | .B disabled 111 | \&. This option is required. 112 | .RE 113 | 114 | .B ratelimit 115 | .I limit 116 | .RS 117 | Defines a ratelimit for the network interface. If ratelimit is set to 0 (default), 118 | no ratelimit will be applied. This setting is optional. 119 | .RE 120 | 121 | .B threshold 122 | .I ttl 123 | .RS 124 | Defines the TTL threshold for the network interface. Packets with a lower TTL than the 125 | threshols value will be ignored. This setting is optional, and by default the threshold is 1. 126 | .RE 127 | 128 | .B altnet 129 | .I networkaddr 130 | \&... 131 | .RS 132 | Defines alternate sources for multicasting and IGMP data. The network address must be on the 133 | following format 'a.b.c.d/n'. By default the router will accept data from sources on the same 134 | network as configured on an interface. If the multicast source lies on a remote network, one 135 | must define from where traffic should be accepted. 136 | 137 | This is especially useful for the upstream interface, since the source for multicast 138 | traffic is often from a remote location. Any number of altnet parameters can be specified. 139 | .RE 140 | 141 | .B whitelist 142 | .I networkaddr 143 | .RS 144 | Defines a whitelist for multicast groups. The network address must be in the following 145 | format 'a.b.c.d/n'. If you want to allow one single group use a network mask of /32, 146 | i.e. 'a.b.c.d/32'. 147 | 148 | By default all multicast groups are allowed on any downstream interface. If at least one 149 | whitelist entry is defined, all igmp membership reports for not explicitly whitelisted 150 | multicast groups will be ignored and therefore not be served by igmpproxy. This is especially 151 | useful, if your provider does only allow a predefined set of multicast groups. These whitelists 152 | are only obeyed by igmpproxy itself, they won't prevent any other igmp client running on the 153 | same machine as igmpproxy from requesting 'unallowed' multicast groups. 154 | 155 | You may specify as many whitelist entries as needed. Although you should keep it as simple as 156 | possible, as this list is parsed for every membership report and therefore this increases igmp 157 | response times. Often used or large groups should be defined first, as parsing ends as soon as 158 | a group matches an entry. 159 | 160 | You may also specify whitelist entries for the upstream interface. Only igmp membership reports 161 | for explicitly whitelisted multicast groups will be sent out on the upstream interface. This 162 | is useful if you want to use multicast groups only between your downstream interfaces, like SSDP 163 | from a UPnP server. 164 | 165 | This option can be combined with 166 | .B blacklist 167 | for fine-grained control. 168 | .RE 169 | 170 | .B blacklist 171 | .I networkaddr 172 | .RS 173 | Defines a blacklist for multicast groups. Similar to 174 | .B whitelist 175 | except that if a blacklist entry is defined, all igmp membership reports for 176 | that multicast group will be ignored and therefore not be served by igmpproxy. 177 | 178 | Each time a multicast group is forwarded or requested, whitelist and blacklist 179 | entries are evaluated in sequential order, from first to last. The last matching 180 | entry decides what action is taken; if no entry matches the multicast group, the 181 | default action is to serve. Note that, if at least one whitelist entry is 182 | defined before any blacklist entry, all igmp membership reports for not 183 | explicitly whitelisted multicast groups will be ignored. 184 | .RE 185 | 186 | .SH EXAMPLE 187 | ## Enable quickleave 188 | quickleave 189 | .br 190 | ## Define settings for eth0 (upstream) 191 | .br 192 | phyint eth0 upstream 193 | altnet 10.0.0.0/8 194 | 195 | ## Disable alternate IP on eth0 (eth0:0) 196 | .br 197 | phyint eth0:0 disabled 198 | 199 | ## Define settings for eth1 (downstream) 200 | .br 201 | phyint eth1 downstream ratelimit 0 threshold 1 202 | 203 | ## Define settings for eth2 (also downstream) 204 | .br 205 | phyint eth2 downstream 206 | 207 | 208 | .SH SEE ALSO 209 | .BR igmpproxy (8) 210 | 211 | .SH AUTHOR 212 | Originally written by Johnny Egeland 213 | -------------------------------------------------------------------------------- /igmpproxy.conf: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | # 3 | # Example configuration file for the IgmpProxy 4 | # -------------------------------------------- 5 | # 6 | # The configuration file must define one upstream 7 | # interface, and one or more downstream interfaces. 8 | # 9 | # If multicast traffic originates outside the 10 | # upstream subnet, the "altnet" option can be 11 | # used in order to define legal multicast sources. 12 | # (Se example...) 13 | # 14 | # The "quickleave" should be used to avoid saturation 15 | # of the upstream link. The option should only 16 | # be used if it's absolutely nessecary to 17 | # accurately imitate just one Client. 18 | # 19 | ######################################################## 20 | 21 | ##------------------------------------------------------ 22 | ## Enable Quickleave mode (Sends Leave instantly) 23 | ##------------------------------------------------------ 24 | quickleave 25 | 26 | 27 | ##------------------------------------------------------ 28 | ## Configuration for eth0 (Upstream Interface) 29 | ##------------------------------------------------------ 30 | phyint eth0 upstream ratelimit 0 threshold 1 31 | altnet 10.0.0.0/8 32 | altnet 192.168.0.0/24 33 | 34 | 35 | ##------------------------------------------------------ 36 | ## Configuration for eth1 (Downstream Interface) 37 | ##------------------------------------------------------ 38 | phyint eth1 downstream ratelimit 0 threshold 1 39 | 40 | 41 | ##------------------------------------------------------ 42 | ## Configuration for eth2 (Disabled Interface) 43 | ##------------------------------------------------------ 44 | phyint eth2 disabled 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | sbin_PROGRAMS = igmpproxy 2 | igmpproxy_SOURCES = \ 3 | callout.c \ 4 | config.c \ 5 | confread.c \ 6 | ifvc.c \ 7 | igmp.c \ 8 | igmpv3.h \ 9 | igmpproxy.c \ 10 | igmpproxy.h \ 11 | kern.c \ 12 | lib.c \ 13 | mroute-api.c \ 14 | os-dragonfly.h \ 15 | os-freebsd.h \ 16 | os-linux.h \ 17 | os-netbsd.h \ 18 | os-openbsd.h \ 19 | os-qnxnto.h \ 20 | request.c \ 21 | rttable.c \ 22 | syslog.c 23 | -------------------------------------------------------------------------------- /src/callout.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | 35 | 36 | #include "igmpproxy.h" 37 | 38 | /* the code below implements a callout queue */ 39 | static int id = 0; 40 | static struct timeOutQueue *queue = 0; /* pointer to the beginning of timeout queue */ 41 | 42 | struct timeOutQueue { 43 | struct timeOutQueue *next; // Next event in queue 44 | int id; 45 | timer_f func; // function to call 46 | void *data; // Data for function 47 | int time; // Time offset for next event 48 | }; 49 | 50 | // Method for dumping the Queue to the log. 51 | static void debugQueue(void); 52 | 53 | /** 54 | * Initializes the callout queue 55 | */ 56 | void callout_init(void) { 57 | queue = NULL; 58 | } 59 | 60 | /** 61 | * Clears all scheduled timeouts... 62 | */ 63 | void free_all_callouts(void) { 64 | struct timeOutQueue *p; 65 | 66 | while (queue) { 67 | p = queue; 68 | queue = queue->next; 69 | free(p); 70 | } 71 | } 72 | 73 | 74 | /** 75 | * elapsed_time seconds have passed; perform all the events that should 76 | * happen. 77 | */ 78 | void age_callout_queue(int elapsed_time) { 79 | struct timeOutQueue *ptr; 80 | struct timeOutQueue *_queue = NULL; 81 | struct timeOutQueue *last = NULL; 82 | int i = 0; 83 | 84 | for (ptr = queue; ptr; ptr = ptr->next) { 85 | if (ptr->time > elapsed_time) { 86 | ptr->time -= elapsed_time; 87 | break; 88 | } else { 89 | elapsed_time -= ptr->time; 90 | if (_queue == NULL) 91 | _queue = ptr; 92 | last = ptr; 93 | } 94 | } 95 | 96 | queue = ptr; 97 | if (last) { 98 | last->next = NULL; 99 | } 100 | 101 | /* process existing events */ 102 | for (ptr = _queue; ptr; ptr = _queue, i++) { 103 | _queue = _queue->next; 104 | my_log(LOG_DEBUG, 0, "About to call timeout %d (#%d)", ptr->id, i); 105 | if (ptr->func) 106 | ptr->func(ptr->data); 107 | free(ptr); 108 | } 109 | } 110 | 111 | /** 112 | * Return in how many seconds age_callout_queue() would like to be called. 113 | * Return -1 if there are no events pending. 114 | */ 115 | int timer_nextTimer(void) { 116 | if (queue) { 117 | if (queue->time < 0) { 118 | my_log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d", 119 | queue->time); 120 | return 0; 121 | } 122 | return queue->time; 123 | } 124 | return -1; 125 | } 126 | 127 | /** 128 | * Inserts a timer in queue. 129 | * @param delay - Number of seconds the timeout should happen in. 130 | * @param action - The function to call on timeout. 131 | * @param data - Pointer to the function data to supply... 132 | */ 133 | int timer_setTimer(int delay, timer_f action, void *data) { 134 | struct timeOutQueue *ptr, *node, *prev; 135 | int i = 0; 136 | 137 | /* create a node */ 138 | node = (struct timeOutQueue *)malloc(sizeof(struct timeOutQueue)); 139 | if (node == 0) { 140 | my_log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n"); 141 | return -1; 142 | } 143 | node->func = action; 144 | node->data = data; 145 | node->time = delay; 146 | node->next = 0; 147 | node->id = ++id; 148 | 149 | prev = ptr = queue; 150 | 151 | /* insert node in the queue */ 152 | 153 | /* if the queue is empty, insert the node and return */ 154 | if (!queue) { 155 | queue = node; 156 | } 157 | else { 158 | /* chase the pointer looking for the right place */ 159 | while (ptr) { 160 | if (delay < ptr->time) { 161 | // We found the correct node 162 | node->next = ptr; 163 | if (ptr == queue) { 164 | queue = node; 165 | } 166 | else { 167 | prev->next = node; 168 | } 169 | ptr->time -= node->time; 170 | my_log(LOG_DEBUG, 0, 171 | "Created timeout %d (#%d) - delay %d secs", 172 | node->id, i, node->time); 173 | debugQueue(); 174 | return node->id; 175 | } else { 176 | // Continur to check nodes. 177 | delay -= ptr->time; node->time = delay; 178 | prev = ptr; 179 | ptr = ptr->next; 180 | } 181 | i++; 182 | } 183 | prev->next = node; 184 | } 185 | my_log(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs", 186 | node->id, i, node->time); 187 | debugQueue(); 188 | 189 | return node->id; 190 | } 191 | 192 | /** 193 | * returns the time until the timer is scheduled 194 | */ 195 | int timer_leftTimer(int timer_id) { 196 | struct timeOutQueue *ptr; 197 | int left = 0; 198 | 199 | if (!timer_id) 200 | return -1; 201 | 202 | for (ptr = queue; ptr; ptr = ptr->next) { 203 | left += ptr->time; 204 | if (ptr->id == timer_id) { 205 | return left; 206 | } 207 | } 208 | return -1; 209 | } 210 | 211 | /** 212 | * clears the associated timer. Returns 1 if succeeded. 213 | */ 214 | int timer_clearTimer(int timer_id) { 215 | struct timeOutQueue *ptr, *prev; 216 | int i = 0; 217 | 218 | if (!timer_id) 219 | return 0; 220 | 221 | prev = ptr = queue; 222 | 223 | /* 224 | * find the right node, delete it. the subsequent node's time 225 | * gets bumped up 226 | */ 227 | 228 | debugQueue(); 229 | while (ptr) { 230 | if (ptr->id == timer_id) { 231 | /* got the right node */ 232 | 233 | /* unlink it from the queue */ 234 | if (ptr == queue) 235 | queue = queue->next; 236 | else 237 | prev->next = ptr->next; 238 | 239 | /* increment next node if any */ 240 | if (ptr->next != 0) 241 | (ptr->next)->time += ptr->time; 242 | 243 | if (ptr->data) 244 | free(ptr->data); 245 | my_log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i); 246 | free(ptr); 247 | debugQueue(); 248 | return 1; 249 | } 250 | prev = ptr; 251 | ptr = ptr->next; 252 | i++; 253 | } 254 | // If we get here, the timer was not deleted. 255 | my_log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i); 256 | debugQueue(); 257 | return 0; 258 | } 259 | 260 | /** 261 | * debugging utility 262 | */ 263 | static void debugQueue(void) { 264 | struct timeOutQueue *ptr; 265 | 266 | for (ptr = queue; ptr; ptr = ptr->next) { 267 | my_log(LOG_DEBUG, 0, "(Id:%d, Time:%d) ", ptr->id, ptr->time); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * config.c - Contains functions to load and parse config 36 | * file, and functions to configure the daemon. 37 | */ 38 | 39 | #include "igmpproxy.h" 40 | 41 | // Structure to keep configuration for VIFs... 42 | struct vifconfig { 43 | char* name; 44 | short state; 45 | int ratelimit; 46 | int threshold; 47 | 48 | // Keep allowed nets for VIF. 49 | struct SubnetList* allowednets; 50 | 51 | // Allowed Groups 52 | struct SubnetList* allowedgroups; 53 | 54 | // Next config in list... 55 | struct vifconfig* next; 56 | }; 57 | 58 | // Structure to keep vif configuration 59 | struct vifconfig *vifconf; 60 | 61 | // Keeps common settings... 62 | static struct Config commonConfig; 63 | 64 | // Prototypes... 65 | struct vifconfig *parsePhyintToken(void); 66 | struct SubnetList *parseSubnetAddress(char *addrstr); 67 | 68 | /** 69 | * Initializes common config.. 70 | */ 71 | static void initCommonConfig(void) { 72 | commonConfig.robustnessValue = DEFAULT_ROBUSTNESS; 73 | commonConfig.queryInterval = INTERVAL_QUERY; 74 | commonConfig.queryResponseInterval = INTERVAL_QUERY_RESPONSE; 75 | 76 | // The defaults are calculated from other settings. 77 | commonConfig.startupQueryInterval = (unsigned int)(INTERVAL_QUERY / 4); 78 | commonConfig.startupQueryCount = DEFAULT_ROBUSTNESS; 79 | 80 | // Default values for leave intervals... 81 | commonConfig.lastMemberQueryInterval = INTERVAL_QUERY_RESPONSE; 82 | commonConfig.lastMemberQueryCount = DEFAULT_ROBUSTNESS; 83 | 84 | // If 1, a leave message is sent upstream on leave messages from downstream. 85 | commonConfig.fastUpstreamLeave = 0; 86 | 87 | // Default size of hash table is 32 bytes (= 256 bits) and can store 88 | // up to the 256 non-collision hosts, approximately half of /24 subnet 89 | commonConfig.downstreamHostsHashTableSize = 32; 90 | 91 | // aimwang: default value 92 | commonConfig.defaultInterfaceState = IF_STATE_DISABLED; 93 | commonConfig.rescanVif = 0; 94 | } 95 | 96 | /** 97 | * Returns a pointer to the common config... 98 | */ 99 | struct Config *getCommonConfig(void) { 100 | return &commonConfig; 101 | } 102 | 103 | /** 104 | * Loads the configuration from file, and stores the config in 105 | * respective holders... 106 | */ 107 | int loadConfig(char *configFile) { 108 | struct vifconfig *tmpPtr; 109 | struct vifconfig **currPtr = &vifconf; 110 | char *token; 111 | 112 | // Initialize common config 113 | initCommonConfig(); 114 | 115 | // Test config file reader... 116 | if(!openConfigFile(configFile)) { 117 | my_log(LOG_ERR, 0, "Unable to open configfile from %s", configFile); 118 | } 119 | 120 | // Get first token... 121 | token = nextConfigToken(); 122 | if(token == NULL) { 123 | my_log(LOG_ERR, 0, "Config file was empty."); 124 | } 125 | 126 | // Loop until all configuration is read. 127 | while ( token != NULL ) { 128 | // Check token... 129 | if(strcmp("phyint", token)==0) { 130 | // Got a phyint token... Call phyint parser 131 | my_log(LOG_DEBUG, 0, "Config: Got a phyint token."); 132 | tmpPtr = parsePhyintToken(); 133 | if(tmpPtr == NULL) { 134 | // Unparsable token... Exit... 135 | closeConfigFile(); 136 | my_log(LOG_WARNING, 0, "Unknown token '%s' in configfile", token); 137 | return 0; 138 | } else { 139 | 140 | my_log(LOG_DEBUG, 0, "IF name : %s", tmpPtr->name); 141 | my_log(LOG_DEBUG, 0, "Next ptr : %x", tmpPtr->next); 142 | my_log(LOG_DEBUG, 0, "Ratelimit : %d", tmpPtr->ratelimit); 143 | my_log(LOG_DEBUG, 0, "Threshold : %d", tmpPtr->threshold); 144 | my_log(LOG_DEBUG, 0, "State : %d", tmpPtr->state); 145 | my_log(LOG_DEBUG, 0, "Allowednet ptr : %x", tmpPtr->allowednets); 146 | 147 | // Insert config, and move temppointer to next location... 148 | *currPtr = tmpPtr; 149 | currPtr = &tmpPtr->next; 150 | } 151 | } 152 | else if(strcmp("quickleave", token)==0) { 153 | // Got a quickleave token.... 154 | my_log(LOG_DEBUG, 0, "Config: Quick leave mode enabled."); 155 | commonConfig.fastUpstreamLeave = 1; 156 | 157 | // Read next token... 158 | token = nextConfigToken(); 159 | continue; 160 | } 161 | else if(strcmp("hashtablesize", token)==0) { 162 | // Got a hashtablesize token... 163 | token = nextConfigToken(); 164 | my_log(LOG_DEBUG, 0, "Config: hashtablesize for quickleave is %s.", token); 165 | if(!commonConfig.fastUpstreamLeave) { 166 | closeConfigFile(); 167 | my_log(LOG_ERR, 0, "Config: hashtablesize is specified but quickleave not enabled."); 168 | return 0; 169 | } 170 | int intToken = atoi(token); 171 | if(intToken < 1 || intToken > 536870912) { 172 | closeConfigFile(); 173 | my_log(LOG_ERR, 0, "Config: hashtablesize must be between 1 and 536870912 bytes."); 174 | return 0; 175 | } 176 | commonConfig.downstreamHostsHashTableSize = intToken; 177 | 178 | // Read next token... 179 | token = nextConfigToken(); 180 | continue; 181 | } 182 | else if(strcmp("defaultdown", token)==0) { 183 | // Got a defaultdown token... 184 | my_log(LOG_DEBUG, 0, "Config: interface Default as down stream."); 185 | commonConfig.defaultInterfaceState = IF_STATE_DOWNSTREAM; 186 | 187 | // Read next token... 188 | token = nextConfigToken(); 189 | continue; 190 | } 191 | else if(strcmp("rescanvif", token)==0) { 192 | // Got a rescanvif token... 193 | my_log(LOG_DEBUG, 0, "Config: Need detect new interface."); 194 | commonConfig.rescanVif = 1; 195 | 196 | // Read next token... 197 | token = nextConfigToken(); 198 | continue; 199 | } 200 | else if(strcmp("chroot", token)==0) { 201 | // path is in next token 202 | token = nextConfigToken(); 203 | 204 | if (snprintf(commonConfig.chroot, sizeof(commonConfig.chroot), "%s", 205 | token) >= (int)sizeof(commonConfig.chroot)) 206 | my_log(LOG_ERR, 0, "Config: chroot is truncated"); 207 | 208 | my_log(LOG_DEBUG, 0, "Config: chroot set to %s", 209 | commonConfig.chroot); 210 | token = nextConfigToken(); 211 | continue; 212 | } 213 | else if(strcmp("user", token)==0) { 214 | // username is in next token 215 | token = nextConfigToken(); 216 | 217 | if (snprintf(commonConfig.user, sizeof(commonConfig.user), "%s", 218 | token) >= (int)sizeof(commonConfig.user)) 219 | my_log(LOG_ERR, 0, "Config: user is truncated"); 220 | 221 | my_log(LOG_DEBUG, 0, "Config: user set to %s", commonConfig.user); 222 | token = nextConfigToken(); 223 | continue; 224 | } else { 225 | // Unparsable token... Exit... 226 | closeConfigFile(); 227 | my_log(LOG_WARNING, 0, "Unknown token '%s' in configfile", token); 228 | return 0; 229 | } 230 | // Get token that was not recognized by phyint parser. 231 | token = getCurrentConfigToken(); 232 | } 233 | 234 | // Close the configfile... 235 | closeConfigFile(); 236 | 237 | return 1; 238 | } 239 | 240 | /** 241 | * Appends extra VIF configuration from config file. 242 | */ 243 | void configureVifs(void) { 244 | unsigned Ix; 245 | struct IfDesc *Dp; 246 | struct vifconfig *confPtr; 247 | 248 | // If no config is available, just return... 249 | if(vifconf == NULL) { 250 | return; 251 | } 252 | 253 | // Loop through all VIFs... 254 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 255 | if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 256 | 257 | // Now try to find a matching config... 258 | for( confPtr = vifconf; confPtr; confPtr = confPtr->next) { 259 | 260 | // I the VIF names match... 261 | if(strcmp(Dp->Name, confPtr->name)==0) { 262 | struct SubnetList *vifLast; 263 | 264 | my_log(LOG_DEBUG, 0, "Found config for %s", Dp->Name); 265 | 266 | 267 | // Set the VIF state 268 | Dp->state = confPtr->state; 269 | 270 | Dp->threshold = confPtr->threshold; 271 | Dp->ratelimit = confPtr->ratelimit; 272 | 273 | // Go to last allowed net on VIF... 274 | for(vifLast = Dp->allowednets; vifLast->next; vifLast = vifLast->next); 275 | 276 | // Insert the configured nets... 277 | vifLast->next = confPtr->allowednets; 278 | 279 | Dp->allowedgroups = confPtr->allowedgroups; 280 | 281 | break; 282 | } 283 | } 284 | } 285 | } 286 | } 287 | 288 | 289 | /** 290 | * Internal function to parse phyint config 291 | */ 292 | struct vifconfig *parsePhyintToken(void) { 293 | struct vifconfig *tmpPtr; 294 | struct SubnetList **anetPtr, **agrpPtr; 295 | char *token; 296 | short parseError = 0; 297 | 298 | // First token should be the interface name.... 299 | token = nextConfigToken(); 300 | 301 | // Sanitycheck the name... 302 | if(token == NULL) return NULL; 303 | if(strlen(token) >= IF_NAMESIZE) return NULL; 304 | my_log(LOG_DEBUG, 0, "Config: IF: Config for interface %s.", token); 305 | 306 | // Allocate memory for configuration... 307 | tmpPtr = (struct vifconfig*)malloc(sizeof(struct vifconfig)); 308 | if(tmpPtr == NULL) { 309 | my_log(LOG_ERR, 0, "Out of memory."); 310 | } 311 | 312 | // Set default values... 313 | tmpPtr->next = NULL; // Important to avoid seg fault... 314 | tmpPtr->ratelimit = 0; 315 | tmpPtr->threshold = 1; 316 | tmpPtr->state = commonConfig.defaultInterfaceState; 317 | tmpPtr->allowednets = NULL; 318 | tmpPtr->allowedgroups = NULL; 319 | 320 | // Make a copy of the token to store the IF name 321 | tmpPtr->name = strdup( token ); 322 | if(tmpPtr->name == NULL) { 323 | my_log(LOG_ERR, 0, "Out of memory."); 324 | } 325 | 326 | // Set the altnet pointer to the allowednets pointer. 327 | anetPtr = &tmpPtr->allowednets; 328 | agrpPtr = &tmpPtr->allowedgroups; 329 | 330 | // Parse the rest of the config.. 331 | token = nextConfigToken(); 332 | while(token != NULL) { 333 | if(strcmp("altnet", token)==0) { 334 | // Altnet... 335 | token = nextConfigToken(); 336 | my_log(LOG_DEBUG, 0, "Config: IF: Got altnet token %s.",token); 337 | 338 | *anetPtr = parseSubnetAddress(token); 339 | if(*anetPtr == NULL) { 340 | parseError = 1; 341 | my_log(LOG_WARNING, 0, "Unable to parse subnet address."); 342 | break; 343 | } else { 344 | anetPtr = &(*anetPtr)->next; 345 | } 346 | } 347 | else if(strcmp("whitelist", token)==0) { 348 | // Whitelist 349 | token = nextConfigToken(); 350 | my_log(LOG_DEBUG, 0, "Config: IF: Got whitelist token %s.", token); 351 | 352 | *agrpPtr = parseSubnetAddress(token); 353 | if(*agrpPtr == NULL) { 354 | free(tmpPtr->name); 355 | free(tmpPtr); 356 | my_log(LOG_ERR, 0, "Unable to parse subnet address."); 357 | } else { 358 | (*agrpPtr)->allow = true; 359 | agrpPtr = &(*agrpPtr)->next; 360 | } 361 | } 362 | else if(strcmp("blacklist", token)==0) { 363 | // Blacklist 364 | token = nextConfigToken(); 365 | my_log(LOG_DEBUG, 0, "Config: IF: Got blacklist token %s.", token); 366 | 367 | *agrpPtr = parseSubnetAddress(token); 368 | if(*agrpPtr == NULL) { 369 | free(tmpPtr->name); 370 | free(tmpPtr); 371 | my_log(LOG_ERR, 0, "Unable to parse subnet address."); 372 | } else { 373 | (*agrpPtr)->allow = false; 374 | agrpPtr = &(*agrpPtr)->next; 375 | } 376 | } 377 | else if(strcmp("upstream", token)==0) { 378 | // Upstream 379 | my_log(LOG_DEBUG, 0, "Config: IF: Got upstream token."); 380 | tmpPtr->state = IF_STATE_UPSTREAM; 381 | } 382 | else if(strcmp("downstream", token)==0) { 383 | // Downstream 384 | my_log(LOG_DEBUG, 0, "Config: IF: Got downstream token."); 385 | tmpPtr->state = IF_STATE_DOWNSTREAM; 386 | } 387 | else if(strcmp("disabled", token)==0) { 388 | // Disabled 389 | my_log(LOG_DEBUG, 0, "Config: IF: Got disabled token."); 390 | tmpPtr->state = IF_STATE_DISABLED; 391 | } 392 | else if(strcmp("ratelimit", token)==0) { 393 | // Ratelimit 394 | token = nextConfigToken(); 395 | my_log(LOG_DEBUG, 0, "Config: IF: Got ratelimit token '%s'.", token); 396 | tmpPtr->ratelimit = atoi( token ); 397 | if(tmpPtr->ratelimit < 0) { 398 | my_log(LOG_WARNING, 0, "Ratelimit must be 0 or more."); 399 | parseError = 1; 400 | break; 401 | } 402 | } 403 | else if(strcmp("threshold", token)==0) { 404 | // Threshold 405 | token = nextConfigToken(); 406 | my_log(LOG_DEBUG, 0, "Config: IF: Got threshold token '%s'.", token); 407 | tmpPtr->threshold = atoi( token ); 408 | if(tmpPtr->threshold <= 0 || tmpPtr->threshold > 255) { 409 | my_log(LOG_WARNING, 0, "Threshold must be between 1 and 255."); 410 | parseError = 1; 411 | break; 412 | } 413 | } 414 | else { 415 | // Unknown token. Break... 416 | break; 417 | } 418 | token = nextConfigToken(); 419 | } 420 | 421 | // Clean up after a parseerror... 422 | if(parseError) { 423 | free(tmpPtr->name); 424 | free(tmpPtr); 425 | tmpPtr = NULL; 426 | } 427 | 428 | return tmpPtr; 429 | } 430 | 431 | /** 432 | * Parses a subnet address string on the format 433 | * a.b.c.d/n into a SubnetList entry. 434 | */ 435 | struct SubnetList *parseSubnetAddress(char *addrstr) { 436 | struct SubnetList *tmpSubnet; 437 | char *tmpStr; 438 | uint32_t addr = 0x00000000; 439 | uint32_t mask = 0xFFFFFFFF; 440 | 441 | // First get the network part of the address... 442 | tmpStr = strtok(addrstr, "/"); 443 | addr = inet_addr(tmpStr); 444 | 445 | tmpStr = strtok(NULL, "/"); 446 | if(tmpStr != NULL) { 447 | int bitcnt = atoi(tmpStr); 448 | if(bitcnt < 0 || bitcnt > 32) { 449 | my_log(LOG_WARNING, 0, "The bits part of the address is invalid : %d.",tmpStr); 450 | return NULL; 451 | } 452 | 453 | if (bitcnt == 0) 454 | mask = 0; 455 | else 456 | mask <<= (32 - bitcnt); 457 | } 458 | 459 | if(addr == (uint32_t)-1) { 460 | my_log(LOG_WARNING, 0, "Unable to parse address token '%s'.", addrstr); 461 | return NULL; 462 | } 463 | 464 | tmpSubnet = (struct SubnetList*) malloc(sizeof(struct SubnetList)); 465 | tmpSubnet->subnet_addr = addr; 466 | tmpSubnet->subnet_mask = ntohl(mask); 467 | tmpSubnet->next = NULL; 468 | 469 | my_log(LOG_DEBUG, 0, "Config: IF: Altnet: Parsed altnet to %s.", 470 | inetFmts(tmpSubnet->subnet_addr, tmpSubnet->subnet_mask,s1)); 471 | 472 | return tmpSubnet; 473 | } 474 | -------------------------------------------------------------------------------- /src/confread.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * confread.c 36 | * 37 | * Generic config file reader. Used to open a config file, 38 | * and read the tokens from it. The parser is really simple, 39 | * and does no backlogging. This means that no form of 40 | * text escaping and qouting is currently supported. 41 | * '#' chars are read as comments, and the comment lasts until 42 | * a newline or EOF 43 | * 44 | */ 45 | 46 | #include "igmpproxy.h" 47 | 48 | #define READ_BUFFER_SIZE 512 // Inputbuffer size... 49 | 50 | #ifndef MAX_TOKEN_LENGTH 51 | #define MAX_TOKEN_LENGTH 30 // Default max token length 52 | #endif 53 | 54 | FILE *confFilePtr; // File handle pointer 55 | char *iBuffer; // Inputbuffer for reading... 56 | unsigned int bufPtr; // Buffer position pointer. 57 | unsigned int readSize; // Number of bytes in buffer after last read... 58 | char cToken[MAX_TOKEN_LENGTH]; // Token buffer... 59 | short validToken; 60 | 61 | /** 62 | * Opens config file specified by filename. 63 | */ 64 | int openConfigFile(char *filename) { 65 | 66 | // Set the buffer to null initially... 67 | iBuffer = NULL; 68 | 69 | // Open the file for reading... 70 | confFilePtr = fopen(filename, "r"); 71 | 72 | // On error, return false 73 | if(confFilePtr == NULL) { 74 | return 0; 75 | } 76 | 77 | // Allocate memory for inputbuffer... 78 | iBuffer = (char*) malloc( sizeof(char) * READ_BUFFER_SIZE ); 79 | 80 | if(iBuffer == NULL) { 81 | closeConfigFile(); 82 | return 0; 83 | } 84 | 85 | // Reset bufferpointer and readsize 86 | bufPtr = 0; 87 | readSize = 0; 88 | 89 | return 1; 90 | } 91 | 92 | /** 93 | * Closes the currently open config file. 94 | */ 95 | void closeConfigFile(void) { 96 | // Close the file. 97 | if(confFilePtr!=NULL) { 98 | fclose(confFilePtr); 99 | } 100 | // Free input buffer memory... 101 | if(iBuffer != NULL) { 102 | free(iBuffer); 103 | } 104 | } 105 | 106 | /** 107 | * Returns the next token from the configfile. The function 108 | * return NULL if there are no more tokens in the file. 109 | */ 110 | char *nextConfigToken(void) { 111 | 112 | validToken = 0; 113 | 114 | // If no file or buffer, return NULL 115 | if(confFilePtr == NULL || iBuffer == NULL) { 116 | return NULL; 117 | } 118 | 119 | { 120 | unsigned int tokenPtr = 0; 121 | unsigned short finished = 0; 122 | unsigned short commentFound = 0; 123 | 124 | // Outer buffer fill loop... 125 | while ( !finished ) { 126 | // If readpointer is at the end of the buffer, we should read next chunk... 127 | if(bufPtr == readSize) { 128 | // Fill up the buffer... 129 | readSize = fread (iBuffer, sizeof(char), READ_BUFFER_SIZE, confFilePtr); 130 | bufPtr = 0; 131 | 132 | // If the readsize is 0, we should just return... 133 | if(readSize == 0) { 134 | return NULL; 135 | } 136 | } 137 | 138 | // Inner char loop... 139 | while ( bufPtr < readSize && !finished ) { 140 | 141 | //printf("Char %s", iBuffer[bufPtr]); 142 | 143 | // Break loop on \0 144 | if(iBuffer[bufPtr] == '\0') { 145 | break; 146 | } 147 | 148 | if( commentFound ) { 149 | if( iBuffer[bufPtr] == '\n' ) { 150 | commentFound = 0; 151 | } 152 | } else { 153 | 154 | // Check current char... 155 | switch(iBuffer[bufPtr]) { 156 | case '#': 157 | // Found a comment start... 158 | commentFound = 1; 159 | break; 160 | 161 | case '\n': 162 | case '\r': 163 | case '\t': 164 | case ' ': 165 | // Newline, CR, Tab and space are end of token, or ignored. 166 | if(tokenPtr > 0) { 167 | cToken[tokenPtr] = '\0'; // EOL 168 | finished = 1; 169 | } 170 | break; 171 | 172 | default: 173 | // Append char to token... 174 | cToken[tokenPtr++] = iBuffer[bufPtr]; 175 | break; 176 | } 177 | } 178 | 179 | // Check end of token buffer !!! 180 | if(tokenPtr == MAX_TOKEN_LENGTH - 1) { 181 | // Prevent buffer overrun... 182 | cToken[tokenPtr] = '\0'; 183 | finished = 1; 184 | } 185 | 186 | // Next char... 187 | bufPtr++; 188 | } 189 | // If the readsize is less than buffersize, we assume EOF. 190 | if(readSize < READ_BUFFER_SIZE && bufPtr == readSize) { 191 | if (tokenPtr > 0) 192 | finished = 1; 193 | else 194 | return NULL; 195 | } 196 | } 197 | if(tokenPtr>0) { 198 | validToken = 1; 199 | return cToken; 200 | } 201 | } 202 | return NULL; 203 | } 204 | 205 | 206 | /** 207 | * Returns the currently active token, or null 208 | * if no tokens are available. 209 | */ 210 | char *getCurrentConfigToken(void) { 211 | return validToken ? cToken : NULL; 212 | } 213 | -------------------------------------------------------------------------------- /src/ifvc.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | 35 | #include "igmpproxy.h" 36 | 37 | /* We need a temporary copy to not break strict aliasing rules */ 38 | static inline uint32_t s_addr_from_sockaddr(const struct sockaddr *addr) { 39 | struct sockaddr_in addr_in; 40 | memcpy(&addr_in, addr, sizeof(addr_in)); 41 | return addr_in.sin_addr.s_addr; 42 | } 43 | 44 | struct IfDesc IfDescVc[ MAX_IF ], *IfDescEp = IfDescVc; 45 | 46 | /* aimwang: add for detect interface and rebuild IfVc record */ 47 | /*************************************************** 48 | * TODO: Only need run me when detect downstream changed. 49 | * For example: /etc/ppp/ip-up & ip-down can touch a file /tmp/ppp_changed 50 | * So I can check if the file exist then run me and delete the file. 51 | ***************************************************/ 52 | void rebuildIfVc () { 53 | struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; 54 | struct ifreq *IfEp; 55 | struct ifconf IoCtlReq; 56 | struct IfDesc *Dp; 57 | struct ifreq *IfPt, *IfNext; 58 | uint32_t addr, subnet, mask; 59 | int Sock; 60 | 61 | // Get the config. 62 | struct Config *config = getCommonConfig(); 63 | 64 | if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) 65 | my_log( LOG_ERR, errno, "RAW socket open" ); 66 | 67 | // aimwang: set all downstream IF as lost, for check IF exist or gone. 68 | for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { 69 | if (Dp->state == IF_STATE_DOWNSTREAM) { 70 | Dp->state = IF_STATE_LOST; 71 | } 72 | } 73 | 74 | IoCtlReq.ifc_buf = (void *)IfVc; 75 | IoCtlReq.ifc_len = sizeof( IfVc ); 76 | 77 | if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) 78 | my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); 79 | 80 | IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); 81 | 82 | for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { 83 | struct ifreq IfReq; 84 | char FmtBu[ 32 ]; 85 | 86 | IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + 87 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 88 | IfPt->ifr_addr.sa_len 89 | #else 90 | sizeof(struct sockaddr_in) 91 | #endif 92 | ); 93 | if (IfNext < IfPt + 1) 94 | IfNext = IfPt + 1; 95 | 96 | for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { 97 | if (0 == strcmp(Dp->Name, IfPt->ifr_name)) { 98 | break; 99 | } 100 | } 101 | 102 | if (Dp == IfDescEp) { 103 | strncpy( Dp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); 104 | } 105 | 106 | if ( IfPt->ifr_addr.sa_family != AF_INET ) { 107 | if (Dp == IfDescEp) { 108 | IfDescEp++; 109 | } 110 | Dp->InAdr.s_addr = 0; /* mark as non-IP interface */ 111 | continue; 112 | } 113 | 114 | // Get the interface adress... 115 | Dp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); 116 | addr = Dp->InAdr.s_addr; 117 | 118 | memcpy( IfReq.ifr_name, Dp->Name, sizeof( IfReq.ifr_name ) ); 119 | 120 | if (ioctl(Sock, SIOCGIFINDEX, &IfReq ) < 0) 121 | my_log(LOG_ERR, errno, "ioctl SIOCGIFINDEX for %s", IfReq.ifr_name); 122 | Dp->ifIndex = IfReq.ifr_ifindex; 123 | 124 | // Get the subnet mask... 125 | if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) 126 | my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); 127 | mask = s_addr_from_sockaddr(&IfReq.ifr_addr); // Do not use ifr_netmask as it is not available on freebsd 128 | subnet = addr & mask; 129 | 130 | if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) 131 | my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); 132 | Dp->Flags = IfReq.ifr_flags; 133 | 134 | if (0x10d1 == Dp->Flags) 135 | { 136 | if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) 137 | my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); 138 | addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); 139 | subnet = addr & mask; 140 | } 141 | 142 | if (Dp == IfDescEp) { 143 | // Insert the verified subnet as an allowed net... 144 | Dp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); 145 | if(IfDescEp->allowednets == NULL) { 146 | my_log(LOG_ERR, 0, "Out of memory !"); 147 | } 148 | Dp->allowednets->next = NULL; 149 | Dp->state = IF_STATE_DOWNSTREAM; 150 | Dp->robustness = DEFAULT_ROBUSTNESS; 151 | Dp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ 152 | Dp->ratelimit = DEFAULT_RATELIMIT; 153 | } 154 | 155 | // Set the network address for the IF.. 156 | Dp->allowednets->subnet_mask = mask; 157 | Dp->allowednets->subnet_addr = subnet; 158 | 159 | // Set the state for the IF... 160 | if (Dp->state == IF_STATE_LOST) { 161 | Dp->state = IF_STATE_DOWNSTREAM; 162 | } 163 | 164 | // when IF become enabeld from downstream, addVIF to enable its VIF 165 | if (Dp->state == IF_STATE_HIDDEN) { 166 | my_log(LOG_NOTICE, 0, "%s [Hidden -> Downstream]", Dp->Name); 167 | Dp->state = IF_STATE_DOWNSTREAM; 168 | addVIF(Dp); 169 | k_join(Dp, allrouters_group); 170 | } 171 | 172 | // addVIF when found new IF 173 | if (Dp == IfDescEp) { 174 | my_log(LOG_NOTICE, 0, "%s [New]", Dp->Name); 175 | Dp->state = config->defaultInterfaceState; 176 | addVIF(Dp); 177 | k_join(Dp, allrouters_group); 178 | IfDescEp++; 179 | } 180 | 181 | // Debug log the result... 182 | my_log( LOG_DEBUG, 0, "rebuildIfVc: Interface %s Index: %d Addr: %s, Flags: 0x%04x, Network: %s", 183 | Dp->Name, 184 | Dp->ifIndex, 185 | fmtInAdr( FmtBu, Dp->InAdr ), 186 | Dp->Flags, 187 | inetFmts(subnet, mask, s1)); 188 | } 189 | 190 | // aimwang: search not longer exist IF, set as hidden and call delVIF 191 | for (Dp = IfDescVc; Dp < IfDescEp; Dp++) { 192 | if (IF_STATE_LOST == Dp->state) { 193 | my_log(LOG_NOTICE, 0, "%s [Downstream -> Hidden]", Dp->Name); 194 | Dp->state = IF_STATE_HIDDEN; 195 | k_leave(Dp, allrouters_group); 196 | delVIF(Dp); 197 | } 198 | } 199 | 200 | close( Sock ); 201 | } 202 | 203 | /* 204 | ** Builds up a vector with the interface of the machine. Calls to the other functions of 205 | ** the module will fail if they are called before the vector is build. 206 | ** 207 | */ 208 | void buildIfVc(void) { 209 | struct ifreq IfVc[ sizeof( IfDescVc ) / sizeof( IfDescVc[ 0 ] ) ]; 210 | struct ifreq *IfEp; 211 | struct Config *config = getCommonConfig(); 212 | 213 | int Sock; 214 | 215 | if ( (Sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) 216 | my_log( LOG_ERR, errno, "RAW socket open" ); 217 | 218 | /* get If vector 219 | */ 220 | { 221 | struct ifconf IoCtlReq; 222 | 223 | IoCtlReq.ifc_buf = (void *)IfVc; 224 | IoCtlReq.ifc_len = sizeof( IfVc ); 225 | 226 | if ( ioctl( Sock, SIOCGIFCONF, &IoCtlReq ) < 0 ) 227 | my_log( LOG_ERR, errno, "ioctl SIOCGIFCONF" ); 228 | 229 | IfEp = (void *)((char *)IfVc + IoCtlReq.ifc_len); 230 | } 231 | 232 | /* loop over interfaces and copy interface info to IfDescVc 233 | */ 234 | { 235 | struct ifreq *IfPt, *IfNext; 236 | 237 | // Temp keepers of interface params... 238 | uint32_t addr, subnet, mask; 239 | 240 | for ( IfPt = IfVc; IfPt < IfEp; IfPt = IfNext ) { 241 | struct ifreq IfReq; 242 | char FmtBu[ 32 ]; 243 | 244 | IfNext = (struct ifreq *)((char *)&IfPt->ifr_addr + 245 | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 246 | IfPt->ifr_addr.sa_len 247 | #else 248 | sizeof(struct sockaddr_in) 249 | #endif 250 | ); 251 | if (IfNext < IfPt + 1) 252 | IfNext = IfPt + 1; 253 | 254 | strncpy( IfDescEp->Name, IfPt->ifr_name, sizeof( IfDescEp->Name ) ); 255 | 256 | // Currently don't set any allowed nets... 257 | //IfDescEp->allowednets = NULL; 258 | 259 | // Set the index to -1 by default. 260 | IfDescEp->index = (unsigned int)-1; 261 | 262 | /* don't retrieve more info for non-IP interfaces 263 | */ 264 | if ( IfPt->ifr_addr.sa_family != AF_INET ) { 265 | IfDescEp->InAdr.s_addr = 0; /* mark as non-IP interface */ 266 | IfDescEp++; 267 | continue; 268 | } 269 | 270 | // Get the interface adress... 271 | IfDescEp->InAdr.s_addr = s_addr_from_sockaddr(&IfPt->ifr_addr); 272 | addr = IfDescEp->InAdr.s_addr; 273 | 274 | memcpy( IfReq.ifr_name, IfDescEp->Name, sizeof( IfReq.ifr_name ) ); 275 | 276 | if (ioctl(Sock, SIOCGIFINDEX, &IfReq ) < 0) 277 | my_log(LOG_ERR, errno, "ioctl SIOCGIFINDEX for %s", IfReq.ifr_name); 278 | IfDescEp->ifIndex = IfReq.ifr_ifindex; 279 | 280 | // Get the subnet mask... 281 | if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) 282 | my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); 283 | mask = s_addr_from_sockaddr(&IfReq.ifr_addr); // Do not use ifr_netmask as it is not available on freebsd 284 | subnet = addr & mask; 285 | 286 | /* get if flags 287 | ** 288 | ** typical flags: 289 | ** lo 0x0049 -> Running, Loopback, Up 290 | ** ethx 0x1043 -> Multicast, Running, Broadcast, Up 291 | ** ipppx 0x0091 -> NoArp, PointToPoint, Up 292 | ** grex 0x00C1 -> NoArp, Running, Up 293 | ** ipipx 0x00C1 -> NoArp, Running, Up 294 | */ 295 | if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) 296 | my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); 297 | 298 | IfDescEp->Flags = IfReq.ifr_flags; 299 | 300 | // aimwang: when pppx get dstaddr for use 301 | if (0x10d1 == IfDescEp->Flags) 302 | { 303 | if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) 304 | my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); 305 | addr = s_addr_from_sockaddr(&IfReq.ifr_dstaddr); 306 | subnet = addr & mask; 307 | } 308 | 309 | // Insert the verified subnet as an allowed net... 310 | IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); 311 | if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); 312 | 313 | // Create the network address for the IF.. 314 | IfDescEp->allowednets->next = NULL; 315 | IfDescEp->allowednets->subnet_mask = mask; 316 | IfDescEp->allowednets->subnet_addr = subnet; 317 | 318 | // Set the default params for the IF... 319 | IfDescEp->state = config->defaultInterfaceState; 320 | IfDescEp->robustness = DEFAULT_ROBUSTNESS; 321 | IfDescEp->threshold = DEFAULT_THRESHOLD; /* ttl limit */ 322 | IfDescEp->ratelimit = DEFAULT_RATELIMIT; 323 | 324 | // Debug log the result... 325 | my_log( LOG_DEBUG, 0, "buildIfVc: Interface %s Index: %d Addr: %s, Flags: 0x%04x, Network: %s", 326 | IfDescEp->Name, 327 | IfDescEp->ifIndex, 328 | fmtInAdr( FmtBu, IfDescEp->InAdr ), 329 | IfDescEp->Flags, 330 | inetFmts(subnet,mask, s1)); 331 | 332 | IfDescEp++; 333 | } 334 | } 335 | 336 | close( Sock ); 337 | } 338 | 339 | /* 340 | ** Returns a pointer to the IfDesc of the interface 'IfName' 341 | ** 342 | ** returns: - pointer to the IfDesc of the requested interface 343 | ** - NULL if no interface 'IfName' exists 344 | ** 345 | */ 346 | struct IfDesc *getIfByName( const char *IfName ) { 347 | struct IfDesc *Dp; 348 | 349 | for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) 350 | if ( ! strcmp( IfName, Dp->Name ) ) 351 | return Dp; 352 | 353 | return NULL; 354 | } 355 | 356 | /* 357 | ** Returns a pointer to the IfDesc of the interface 'Ix' 358 | ** 359 | ** returns: - pointer to the IfDesc of the requested interface 360 | ** - NULL if no interface 'Ix' exists 361 | ** 362 | */ 363 | struct IfDesc *getIfByIx( unsigned Ix ) { 364 | struct IfDesc *Dp = &IfDescVc[ Ix ]; 365 | return Dp < IfDescEp ? Dp : NULL; 366 | } 367 | 368 | /** 369 | * Returns a pointer to the IfDesc whose subnet matches 370 | * the supplied IP adress. The IP must match a interfaces 371 | * subnet, or any configured allowed subnet on a interface. 372 | */ 373 | struct IfDesc *getIfByAddress( uint32_t ipaddr ) { 374 | 375 | struct IfDesc *Dp; 376 | struct SubnetList *currsubnet; 377 | struct IfDesc *res = NULL; 378 | uint32_t last_subnet_mask = 0; 379 | 380 | for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { 381 | // Loop through all registered allowed nets of the VIF... 382 | for(currsubnet = Dp->allowednets; currsubnet != NULL; currsubnet = currsubnet->next) { 383 | // Check if the ip falls in under the subnet.... 384 | if(currsubnet->subnet_mask > last_subnet_mask && (ipaddr & currsubnet->subnet_mask) == currsubnet->subnet_addr) { 385 | res = Dp; 386 | last_subnet_mask = currsubnet->subnet_mask; 387 | } 388 | } 389 | } 390 | return res; 391 | } 392 | 393 | 394 | /** 395 | * Returns a pointer to the IfDesc whose subnet matches 396 | * the supplied IP adress. The IP must match a interfaces 397 | * subnet, or any configured allowed subnet on a interface. 398 | */ 399 | struct IfDesc *getIfByVifIndex( unsigned vifindex ) { 400 | struct IfDesc *Dp; 401 | if(vifindex>0) { 402 | for ( Dp = IfDescVc; Dp < IfDescEp; Dp++ ) { 403 | if(Dp->index == vifindex) { 404 | return Dp; 405 | } 406 | } 407 | } 408 | return NULL; 409 | } 410 | 411 | 412 | /** 413 | * Function that checks if a given ipaddress is a valid 414 | * address for the supplied VIF. 415 | */ 416 | int isAdressValidForIf( struct IfDesc* intrface, uint32_t ipaddr ) { 417 | struct SubnetList *currsubnet; 418 | 419 | if(intrface == NULL) { 420 | return 0; 421 | } 422 | 423 | // Loop through all registered allowed nets of the VIF... 424 | for(currsubnet = intrface->allowednets; currsubnet != NULL; currsubnet = currsubnet->next) { 425 | // Check if the ip falls in under the subnet.... 426 | if((ipaddr & currsubnet->subnet_mask) == (currsubnet->subnet_addr& currsubnet->subnet_mask)) { 427 | return 1; 428 | } 429 | } 430 | return 0; 431 | } 432 | -------------------------------------------------------------------------------- /src/igmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * igmp.h - Recieves IGMP requests, and handle them 36 | * appropriately... 37 | */ 38 | 39 | #include "igmpproxy.h" 40 | #include "igmpv3.h" 41 | 42 | // Globals 43 | uint32_t allhosts_group; /* All hosts addr in net order */ 44 | uint32_t allrouters_group; /* All hosts addr in net order */ 45 | uint32_t alligmp3_group; /* IGMPv3 addr in net order */ 46 | 47 | extern int MRouterFD; 48 | 49 | /* 50 | * Open and initialize the igmp socket, and fill in the non-changing 51 | * IP header fields in the output packet buffer. 52 | */ 53 | void initIgmp(void) { 54 | struct ip *ip; 55 | 56 | recv_buf = malloc(RECV_BUF_SIZE); 57 | send_buf = malloc(RECV_BUF_SIZE); 58 | 59 | k_hdr_include(true); /* include IP header when sending */ 60 | k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */ 61 | k_set_ttl(1); /* restrict multicasts to one hop */ 62 | k_set_loop(false); /* disable multicast loopback */ 63 | 64 | ip = (struct ip *)send_buf; 65 | memset(ip, 0, sizeof(struct ip)); 66 | /* 67 | * Fields zeroed that aren't filled in later: 68 | * - IP ID (let the kernel fill it in) 69 | * - Offset (we don't send fragments) 70 | * - Checksum (let the kernel fill it in) 71 | */ 72 | ip->ip_v = IPVERSION; 73 | ip->ip_hl = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */ 74 | ip->ip_tos = 0xc0; /* Internet Control */ 75 | ip->ip_ttl = MAXTTL; /* applies to unicasts only */ 76 | ip->ip_p = IPPROTO_IGMP; 77 | 78 | allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); 79 | allrouters_group = htonl(INADDR_ALLRTRS_GROUP); 80 | alligmp3_group = htonl(INADDR_ALLIGMPV3_GROUP); 81 | } 82 | 83 | /** 84 | * Finds the textual name of the supplied IGMP request. 85 | */ 86 | static const char *igmpPacketKind(unsigned int type, unsigned int code) { 87 | static char unknown[20]; 88 | 89 | switch (type) { 90 | case IGMP_MEMBERSHIP_QUERY: return "Membership query "; 91 | case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report "; 92 | case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report "; 93 | case IGMP_V3_MEMBERSHIP_REPORT: return "V3 member report "; 94 | case IGMP_V2_LEAVE_GROUP: return "Leave message "; 95 | 96 | default: 97 | sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code); 98 | return unknown; 99 | } 100 | } 101 | 102 | /** 103 | * Process a newly received IGMP packet that is sitting in the input 104 | * packet buffer. 105 | */ 106 | void acceptIgmp(int recvlen) { 107 | register uint32_t src, dst, group; 108 | struct ip *ip; 109 | struct igmp *igmp; 110 | struct igmpv3_report *igmpv3; 111 | struct igmpv3_grec *grec; 112 | int ipdatalen, iphdrlen, ngrec, nsrcs, i; 113 | 114 | if (recvlen < (int)sizeof(struct ip)) { 115 | my_log(LOG_WARNING, 0, 116 | "received packet too short (%u bytes) for IP header", recvlen); 117 | return; 118 | } 119 | 120 | ip = (struct ip *)recv_buf; 121 | src = ip->ip_src.s_addr; 122 | dst = ip->ip_dst.s_addr; 123 | 124 | /* 125 | * this is most likely a message from the kernel indicating that 126 | * a new src grp pair message has arrived and so, it would be 127 | * necessary to install a route into the kernel for this. 128 | */ 129 | if (ip->ip_p == 0) { 130 | if (src == 0 || dst == 0) { 131 | my_log(LOG_WARNING, 0, "kernel request not accurate"); 132 | } 133 | else { 134 | struct IfDesc *checkVIF; 135 | 136 | for(i=0; iInAdr.s_addr) { 147 | my_log(LOG_NOTICE, 0, "Route activation request from %s for %s is from myself. Ignoring.", 148 | inetFmt(src, s1), inetFmt(dst, s2)); 149 | return; 150 | } 151 | else if(!isAdressValidForIf(checkVIF, src)) { 152 | struct IfDesc *downVIF = getIfByAddress(src); 153 | if (downVIF && downVIF->state & IF_STATE_DOWNSTREAM) { 154 | my_log(LOG_NOTICE, 0, "The source address %s for group %s is from downstream VIF[%d]. Ignoring.", 155 | inetFmt(src, s1), inetFmt(dst, s2), i); 156 | } else { 157 | my_log(LOG_WARNING, 0, "The source address %s for group %s, is not in any valid net for upstream VIF[%d].", 158 | inetFmt(src, s1), inetFmt(dst, s2), i); 159 | } 160 | } else { 161 | // Activate the route. 162 | int vifindex = checkVIF->index; 163 | my_log(LOG_DEBUG, 0, "Route activate request from %s to %s on VIF[%d]", 164 | inetFmt(src,s1), inetFmt(dst,s2), vifindex); 165 | activateRoute(dst, src, vifindex); 166 | i = MAX_UPS_VIFS; 167 | } 168 | } else { 169 | i = MAX_UPS_VIFS; 170 | } 171 | } 172 | } 173 | return; 174 | } 175 | 176 | iphdrlen = ip->ip_hl << 2; 177 | ipdatalen = ip_data_len(ip); 178 | 179 | if (iphdrlen + ipdatalen != recvlen) { 180 | my_log(LOG_WARNING, 0, 181 | "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", 182 | inetFmt(src, s1), recvlen, iphdrlen, ipdatalen); 183 | return; 184 | } 185 | 186 | igmp = (struct igmp *)(recv_buf + iphdrlen); 187 | if ((ipdatalen < IGMP_MINLEN) || 188 | (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT && ipdatalen <= IGMPV3_MINLEN)) { 189 | my_log(LOG_WARNING, 0, 190 | "received IP data field too short (%u bytes) for IGMP, from %s", 191 | ipdatalen, inetFmt(src, s1)); 192 | return; 193 | } 194 | 195 | my_log(LOG_NOTICE, 0, "RECV %s from %-15s to %s", 196 | igmpPacketKind(igmp->igmp_type, igmp->igmp_code), 197 | inetFmt(src, s1), inetFmt(dst, s2) ); 198 | 199 | switch (igmp->igmp_type) { 200 | case IGMP_V1_MEMBERSHIP_REPORT: 201 | case IGMP_V2_MEMBERSHIP_REPORT: 202 | group = igmp->igmp_group.s_addr; 203 | acceptGroupReport(src, group); 204 | return; 205 | 206 | case IGMP_V3_MEMBERSHIP_REPORT: 207 | igmpv3 = (struct igmpv3_report *)(recv_buf + iphdrlen); 208 | grec = &igmpv3->igmp_grec[0]; 209 | ngrec = ntohs(igmpv3->igmp_ngrec); 210 | while (ngrec--) { 211 | if ((uint8_t *)igmpv3 + ipdatalen < (uint8_t *)grec + sizeof(*grec)) 212 | break; 213 | group = grec->grec_mca.s_addr; 214 | nsrcs = ntohs(grec->grec_nsrcs); 215 | switch (grec->grec_type) { 216 | case IGMPV3_MODE_IS_INCLUDE: 217 | case IGMPV3_CHANGE_TO_INCLUDE: 218 | if (nsrcs == 0) { 219 | acceptLeaveMessage(src, group); 220 | break; 221 | } /* else fall through */ 222 | case IGMPV3_MODE_IS_EXCLUDE: 223 | case IGMPV3_CHANGE_TO_EXCLUDE: 224 | case IGMPV3_ALLOW_NEW_SOURCES: 225 | acceptGroupReport(src, group); 226 | break; 227 | case IGMPV3_BLOCK_OLD_SOURCES: 228 | break; 229 | default: 230 | my_log(LOG_INFO, 0, 231 | "ignoring unknown IGMPv3 group record type %x from %s to %s for %s", 232 | grec->grec_type, inetFmt(src, s1), inetFmt(dst, s2), 233 | inetFmt(group, s3)); 234 | break; 235 | } 236 | grec = (struct igmpv3_grec *) 237 | (&grec->grec_src[nsrcs] + grec->grec_auxwords * 4); 238 | } 239 | return; 240 | 241 | case IGMP_V2_LEAVE_GROUP: 242 | group = igmp->igmp_group.s_addr; 243 | acceptLeaveMessage(src, group); 244 | return; 245 | 246 | case IGMP_MEMBERSHIP_QUERY: 247 | return; 248 | 249 | default: 250 | my_log(LOG_INFO, 0, 251 | "ignoring unknown IGMP message type %x from %s to %s", 252 | igmp->igmp_type, inetFmt(src, s1), 253 | inetFmt(dst, s2)); 254 | return; 255 | } 256 | } 257 | 258 | 259 | /* 260 | * Construct an IGMP message in the output packet buffer. The caller may 261 | * have already placed data in that buffer, of length 'datalen'. 262 | */ 263 | static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { 264 | struct ip *ip; 265 | struct igmp *igmp; 266 | extern int curttl; 267 | 268 | ip = (struct ip *)send_buf; 269 | ip->ip_src.s_addr = src; 270 | ip->ip_dst.s_addr = dst; 271 | ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen); 272 | 273 | if (IN_MULTICAST(ntohl(dst))) { 274 | ip->ip_ttl = curttl; 275 | } else { 276 | ip->ip_ttl = MAXTTL; 277 | } 278 | 279 | /* Add Router Alert option */ 280 | ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[0] = IPOPT_RA; 281 | ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[1] = 0x04; 282 | ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00; 283 | ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00; 284 | 285 | igmp = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN); 286 | igmp->igmp_type = type; 287 | igmp->igmp_code = code; 288 | igmp->igmp_group.s_addr = group; 289 | igmp->igmp_cksum = 0; 290 | igmp->igmp_cksum = inetChksum((unsigned short *)igmp, 291 | IP_HEADER_RAOPT_LEN + datalen); 292 | 293 | } 294 | 295 | /* 296 | * Call build_igmp() to build an IGMP message in the output packet buffer. 297 | * Then send the message from the interface with IP address 'src' to 298 | * destination 'dst'. If struct ip_mreqn is present on the target OS, it is 299 | * used instead of 'src' to select the sending interface. 'src' is still used 300 | * as the source IP. 301 | */ 302 | void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen, int ifidx) { 303 | struct sockaddr_in sdst; 304 | int setloop = 0, setigmpsource = 0; 305 | 306 | buildIgmp(src, dst, type, code, group, datalen); 307 | 308 | if (IN_MULTICAST(ntohl(dst))) { 309 | k_set_if(src, ifidx); 310 | setigmpsource = 1; 311 | if (type != IGMP_DVMRP || dst == allhosts_group) { 312 | setloop = 1; 313 | k_set_loop(true); 314 | } 315 | } 316 | 317 | memset(&sdst, 0, sizeof(sdst)); 318 | sdst.sin_family = AF_INET; 319 | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 320 | sdst.sin_len = sizeof(sdst); 321 | #endif 322 | sdst.sin_addr.s_addr = dst; 323 | if (sendto(MRouterFD, send_buf, 324 | IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0, 325 | (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { 326 | if (errno == ENETDOWN) 327 | my_log(LOG_ERR, errno, "Sender VIF was down."); 328 | else 329 | my_log(LOG_INFO, errno, 330 | "sendto to %s on %s", 331 | inetFmt(dst, s1), inetFmt(src, s2)); 332 | } 333 | 334 | if(setigmpsource) { 335 | if (setloop) { 336 | k_set_loop(false); 337 | } 338 | // Restore original... 339 | k_set_if(INADDR_ANY, 0); 340 | } 341 | 342 | my_log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", 343 | igmpPacketKind(type, code), 344 | src == INADDR_ANY ? "INADDR_ANY" : inetFmt(src, s1), inetFmt(dst, s2)); 345 | } 346 | -------------------------------------------------------------------------------- /src/igmpproxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * igmpproxy.c - The main file for the IGMP proxy application. 36 | * 37 | * February 2005 - Johnny Egeland 38 | */ 39 | 40 | /* getopt() and clock_getime() */ 41 | #ifndef _POSIX_C_SOURCE 42 | #define _POSIX_C_SOURCE 200112L 43 | #endif 44 | 45 | #include "igmpproxy.h" 46 | 47 | static const char Usage[] = 48 | "Usage: igmpproxy [-h] [-n] [-d] [-v [-v]] \n" 49 | "\n" 50 | " -h Display this help screen\n" 51 | " -n Do not run as a daemon\n" 52 | " -d Run in debug mode. Output all messages on stderr. Implies -n.\n" 53 | " -v Be verbose. Give twice to see even debug messages.\n" 54 | "\n" 55 | PACKAGE_STRING "\n" 56 | ; 57 | 58 | // Local function Prototypes 59 | static void signalHandler(int); 60 | int igmpProxyInit(void); 61 | void igmpProxyCleanUp(void); 62 | void igmpProxyRun(void); 63 | 64 | // Global vars... 65 | static int sighandled = 0; 66 | #define GOT_SIGINT 0x01 67 | #define GOT_SIGHUP 0x02 68 | #define GOT_SIGUSR1 0x04 69 | #define GOT_SIGUSR2 0x08 70 | 71 | // Holds the indeces of the upstream IF... 72 | int upStreamIfIdx[MAX_UPS_VIFS]; 73 | 74 | /** 75 | * Program main method. Is invoked when the program is started 76 | * on commandline. The number of commandline arguments, and a 77 | * pointer to the arguments are received on the line... 78 | */ 79 | int main( int ArgCn, char *ArgVc[] ) { 80 | 81 | int c, devnull = -1; 82 | bool NotAsDaemon = false; 83 | 84 | struct Config *config = NULL; 85 | struct passwd *pw = NULL; 86 | 87 | srand(time(NULL) * getpid()); 88 | 89 | // Parse the commandline options and setup basic settings.. 90 | while ((c = getopt(ArgCn, ArgVc, "vdnh")) != -1) { 91 | switch (c) { 92 | case 'n': 93 | NotAsDaemon = true; 94 | break; 95 | case 'd': 96 | Log2Stderr = true; 97 | NotAsDaemon = true; 98 | break; 99 | case 'v': 100 | if (LogLevel == LOG_INFO) 101 | LogLevel = LOG_DEBUG; 102 | else 103 | LogLevel = LOG_INFO; 104 | break; 105 | case 'h': 106 | fputs(Usage, stderr); 107 | exit(0); 108 | break; 109 | default: 110 | exit(1); 111 | break; 112 | } 113 | } 114 | 115 | if (optind != ArgCn - 1) { 116 | fputs("You must specify the configuration file.\n", stderr); 117 | exit(1); 118 | } 119 | char *configFilePath = ArgVc[optind]; 120 | 121 | // Chech that we are root 122 | if (geteuid() != 0) { 123 | fprintf(stderr, "igmpproxy: must be root\n"); 124 | exit(1); 125 | } 126 | 127 | openlog("igmpproxy", LOG_PID, LOG_USER); 128 | 129 | // Write debug notice with file path... 130 | my_log(LOG_DEBUG, 0, "Searching for config file at '%s'" , configFilePath); 131 | 132 | do { 133 | 134 | // Loads the config file... 135 | if( ! loadConfig( configFilePath ) ) { 136 | my_log(LOG_ERR, 0, "Unable to load config file..."); 137 | break; 138 | } 139 | 140 | // Initializes the deamon. 141 | if ( !igmpProxyInit() ) { 142 | my_log(LOG_ERR, 0, "Unable to initialize IGMPproxy."); 143 | break; 144 | } 145 | 146 | // Open /dev/null before chrooting. 147 | if (!NotAsDaemon) { 148 | devnull = open("/dev/null", O_RDWR); 149 | if (devnull == -1) 150 | my_log(LOG_ERR, 0, "unable to open /dev/null"); 151 | } 152 | 153 | config = getCommonConfig(); 154 | 155 | if (config->user[0]) { 156 | pw = getpwnam(config->user); 157 | if (pw == NULL) 158 | my_log(LOG_ERR, 0, "unknown user %s", config->user); 159 | } 160 | 161 | if (config->chroot[0]) 162 | if (chroot(config->chroot) != 0 || chdir("/") != 0) 163 | my_log(LOG_ERR, 0, "unable to chroot to %s", 164 | config->chroot); 165 | 166 | if (pw != NULL) 167 | if (setgroups(1, &pw->pw_gid) != 0 || 168 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0 || 169 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) 170 | my_log(LOG_ERR, 0, "unable to drop privileges to %s", 171 | config->user); 172 | 173 | if ( !NotAsDaemon ) { 174 | 175 | // Only daemon goes past this line... 176 | if (fork()) exit(0); 177 | 178 | // Detach daemon from terminal 179 | if ( close( 0 ) < 0 || close( 1 ) < 0 || close( 2 ) < 0 || 180 | dup2( devnull, 0 ) < 0 || dup2( devnull, 1 ) < 0 || 181 | dup2( devnull, 2 ) < 0 || setpgid( 0, 0 ) < 0 ) { 182 | my_log( LOG_ERR, errno, "failed to detach daemon" ); 183 | } 184 | if (devnull > 2) 185 | close(devnull); 186 | } 187 | 188 | // Go to the main loop. 189 | igmpProxyRun(); 190 | 191 | // Clean up 192 | igmpProxyCleanUp(); 193 | 194 | } while ( false ); 195 | 196 | // Inform that we are exiting. 197 | my_log(LOG_INFO, 0, "Shutdown complete...."); 198 | 199 | exit(0); 200 | } 201 | 202 | /** 203 | * Handles the initial startup of the daemon. 204 | */ 205 | int igmpProxyInit(void) { 206 | struct sigaction sa; 207 | int Err; 208 | 209 | sa.sa_handler = signalHandler; 210 | sa.sa_flags = 0; /* Interrupt system calls */ 211 | sigemptyset(&sa.sa_mask); 212 | sigaction(SIGTERM, &sa, NULL); 213 | sigaction(SIGINT, &sa, NULL); 214 | 215 | // Loads configuration for Physical interfaces... 216 | buildIfVc(); 217 | 218 | // Configures IF states and settings 219 | configureVifs(); 220 | 221 | switch ( Err = enableMRouter() ) { 222 | case 0: break; 223 | case EADDRINUSE: my_log( LOG_ERR, EADDRINUSE, "MC-Router API already in use" ); break; 224 | default: my_log( LOG_ERR, Err, "MRT_INIT failed" ); 225 | } 226 | 227 | /* create VIFs for all IP, non-loop interfaces 228 | */ 229 | { 230 | unsigned Ix; 231 | struct IfDesc *Dp; 232 | int vifcount = 0, upsvifcount = 0; 233 | 234 | // init array to "not set" 235 | for ( Ix = 0; Ix < MAX_UPS_VIFS; Ix++) 236 | { 237 | upStreamIfIdx[Ix] = -1; 238 | } 239 | 240 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 241 | 242 | if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 243 | if(Dp->state == IF_STATE_UPSTREAM) { 244 | if (upsvifcount < MAX_UPS_VIFS -1) 245 | { 246 | my_log(LOG_DEBUG, 0, "Found upstrem IF #%d, will assing as upstream Vif %d", 247 | upsvifcount, Ix); 248 | upStreamIfIdx[upsvifcount++] = Ix; 249 | } else { 250 | my_log(LOG_ERR, 0, "Cannot set VIF #%d as upstream as well. Mac upstream Vif count is %d", 251 | Ix, MAX_UPS_VIFS); 252 | } 253 | } 254 | 255 | if (Dp->state != IF_STATE_DISABLED) { 256 | addVIF( Dp ); 257 | vifcount++; 258 | } 259 | } 260 | } 261 | 262 | if(0 == upsvifcount) { 263 | my_log(LOG_ERR, 0, "There must be at least 1 Vif as upstream."); 264 | } 265 | } 266 | 267 | // Initialize IGMP 268 | initIgmp(); 269 | // Initialize Routing table 270 | initRouteTable(); 271 | // Initialize timer 272 | callout_init(); 273 | 274 | return 1; 275 | } 276 | 277 | /** 278 | * Clean up all on exit... 279 | */ 280 | void igmpProxyCleanUp(void) { 281 | my_log( LOG_DEBUG, 0, "clean handler called" ); 282 | 283 | free_all_callouts(); // No more timeouts. 284 | clearAllRoutes(); // Remove all routes. 285 | disableMRouter(); // Disable the multirout API 286 | } 287 | 288 | /** 289 | * Main daemon loop. 290 | */ 291 | void igmpProxyRun(void) { 292 | // Get the config. 293 | struct Config *config = getCommonConfig(); 294 | // Set some needed values. 295 | register int recvlen; 296 | int MaxFD, Rt, secs; 297 | fd_set ReadFDS; 298 | socklen_t dummy = 0; 299 | struct timespec curtime, lasttime, difftime, tv; 300 | // The timeout is a pointer in order to set it to NULL if nessecary. 301 | struct timespec *timeout = &tv; 302 | 303 | // Initialize timer vars 304 | difftime.tv_nsec = 0; 305 | clock_gettime(CLOCK_MONOTONIC, &curtime); 306 | lasttime = curtime; 307 | 308 | // First thing we send a membership query in downstream VIF's... 309 | sendGeneralMembershipQuery(); 310 | 311 | // Loop until the end... 312 | for (;;) { 313 | 314 | // Process signaling... 315 | if (sighandled) { 316 | if (sighandled & GOT_SIGINT) { 317 | sighandled &= ~GOT_SIGINT; 318 | my_log(LOG_NOTICE, 0, "Got a interrupt signal. Exiting."); 319 | break; 320 | } 321 | } 322 | 323 | /* aimwang: call rebuildIfVc */ 324 | if (config->rescanVif) 325 | rebuildIfVc(); 326 | 327 | // Prepare timeout... 328 | secs = timer_nextTimer(); 329 | if(secs == -1) { 330 | timeout = NULL; 331 | } else { 332 | timeout->tv_nsec = 0; 333 | timeout->tv_sec = (secs > 3) ? 3 : secs; // aimwang: set max timeout 334 | } 335 | 336 | // Prepare for select. 337 | MaxFD = MRouterFD; 338 | 339 | FD_ZERO( &ReadFDS ); 340 | FD_SET( MRouterFD, &ReadFDS ); 341 | 342 | // wait for input 343 | Rt = pselect( MaxFD +1, &ReadFDS, NULL, NULL, timeout, NULL ); 344 | 345 | // log and ignore failures 346 | if( Rt < 0 ) { 347 | my_log( LOG_WARNING, errno, "select() failure" ); 348 | continue; 349 | } 350 | else if( Rt > 0 ) { 351 | 352 | // Read IGMP request, and handle it... 353 | if( FD_ISSET( MRouterFD, &ReadFDS ) ) { 354 | 355 | recvlen = recvfrom(MRouterFD, recv_buf, RECV_BUF_SIZE, 356 | 0, NULL, &dummy); 357 | if (recvlen < 0) { 358 | if (errno != EINTR) my_log(LOG_ERR, errno, "recvfrom"); 359 | continue; 360 | } 361 | 362 | acceptIgmp(recvlen); 363 | } 364 | } 365 | 366 | // At this point, we can handle timeouts... 367 | do { 368 | /* 369 | * If the select timed out, then there's no other 370 | * activity to account for and we don't need to 371 | * call gettimeofday. 372 | */ 373 | if (Rt == 0) { 374 | curtime.tv_sec = lasttime.tv_sec + secs; 375 | curtime.tv_nsec = lasttime.tv_nsec; 376 | Rt = -1; /* don't do this next time through the loop */ 377 | } else { 378 | clock_gettime(CLOCK_MONOTONIC, &curtime); 379 | } 380 | difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; 381 | difftime.tv_nsec += curtime.tv_nsec - lasttime.tv_nsec; 382 | while (difftime.tv_nsec > 1000000000) { 383 | difftime.tv_sec++; 384 | difftime.tv_nsec -= 1000000000; 385 | } 386 | if (difftime.tv_nsec < 0) { 387 | difftime.tv_sec--; 388 | difftime.tv_nsec += 1000000000; 389 | } 390 | lasttime = curtime; 391 | if (secs == 0 || difftime.tv_sec > 0) 392 | age_callout_queue(difftime.tv_sec); 393 | secs = -1; 394 | } while (difftime.tv_sec > 0); 395 | 396 | } 397 | 398 | } 399 | 400 | /* 401 | * Signal handler. Take note of the fact that the signal arrived 402 | * so that the main loop can take care of it. 403 | */ 404 | static void signalHandler(int sig) { 405 | switch (sig) { 406 | case SIGINT: 407 | case SIGTERM: 408 | sighandled |= GOT_SIGINT; 409 | break; 410 | /* XXX: Not in use. 411 | case SIGHUP: 412 | sighandled |= GOT_SIGHUP; 413 | break; 414 | 415 | case SIGUSR1: 416 | sighandled |= GOT_SIGUSR1; 417 | break; 418 | 419 | case SIGUSR2: 420 | sighandled |= GOT_SIGUSR2; 421 | break; 422 | */ 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /src/igmpproxy.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * igmpproxy.h - Header file for common includes. 36 | */ 37 | 38 | #include "config.h" 39 | #include "os.h" 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #include 63 | #include 64 | #include 65 | 66 | /* 67 | * Limit on length of route data 68 | */ 69 | #define MAX_IP_PACKET_LEN 576 70 | #define MIN_IP_HEADER_LEN 20 71 | #define MAX_IP_HEADER_LEN 60 72 | #define IP_HEADER_RAOPT_LEN 24 73 | 74 | #define MAX_UPS_VIFS 8 75 | 76 | // Useful macros.. 77 | #define VCMC( Vc ) (sizeof( Vc ) / sizeof( (Vc)[ 0 ] )) 78 | #define VCEP( Vc ) (&(Vc)[ VCMC( Vc ) ]) 79 | 80 | // Bit manipulation macros... 81 | #define BIT_ZERO(X) ((X) = 0) 82 | #define BIT_SET(X,n) ((X) |= 1 << (n)) 83 | #define BIT_CLR(X,n) ((X) &= ~(1 << (n))) 84 | #define BIT_TST(X,n) ((X) & 1 << (n)) 85 | 86 | 87 | //################################################################################# 88 | // Globals 89 | //################################################################################# 90 | 91 | /* 92 | * External declarations for global variables and functions. 93 | */ 94 | #define RECV_BUF_SIZE 8192 95 | extern char *recv_buf; 96 | extern char *send_buf; 97 | 98 | extern char s1[]; 99 | extern char s2[]; 100 | extern char s3[]; 101 | extern char s4[]; 102 | 103 | 104 | 105 | //################################################################################# 106 | // Lib function prototypes. 107 | //################################################################################# 108 | 109 | /* syslog.c 110 | */ 111 | extern bool Log2Stderr; // Log to stderr instead of to syslog 112 | extern int LogLevel; // Log threshold, LOG_WARNING .... LOG_DEBUG 113 | 114 | void my_log( int Serverity, int Errno, const char *FmtSt, ... ); 115 | 116 | /* ifvc.c 117 | */ 118 | #define MAX_IF 40 // max. number of interfaces recognized 119 | 120 | // Interface states 121 | #define IF_STATE_DISABLED 0 // Interface should be ignored. 122 | #define IF_STATE_UPSTREAM 1 // Interface is the upstream interface 123 | #define IF_STATE_DOWNSTREAM 2 // Interface is a downstream interface 124 | #define IF_STATE_LOST 3 // aimwang: Temp from downstream to hidden 125 | #define IF_STATE_HIDDEN 4 // aimwang: Interface is hidden 126 | 127 | // Multicast default values... 128 | #define DEFAULT_ROBUSTNESS 2 129 | #define DEFAULT_THRESHOLD 1 130 | #define DEFAULT_RATELIMIT 0 131 | 132 | // Define timer constants (in seconds...) 133 | #define INTERVAL_QUERY 125 134 | #define INTERVAL_QUERY_RESPONSE 10 135 | //#define INTERVAL_QUERY_RESPONSE 10 136 | 137 | #define ROUTESTATE_NOTJOINED 0 // The group corresponding to route is not joined 138 | #define ROUTESTATE_JOINED 1 // The group corresponding to route is joined 139 | #define ROUTESTATE_CHECK_LAST_MEMBER 2 // The router is checking for hosts 140 | 141 | 142 | 143 | // Linked list of networks... 144 | struct SubnetList { 145 | uint32_t subnet_addr; 146 | uint32_t subnet_mask; 147 | struct SubnetList *next; 148 | bool allow; 149 | }; 150 | 151 | struct IfDesc { 152 | char Name[IF_NAMESIZE]; 153 | struct in_addr InAdr; /* == 0 for non IP interfaces */ 154 | int ifIndex; 155 | short Flags; 156 | short state; 157 | struct SubnetList* allowednets; 158 | struct SubnetList* allowedgroups; 159 | unsigned int robustness; 160 | unsigned char threshold; /* ttl limit */ 161 | unsigned int ratelimit; 162 | unsigned int index; 163 | }; 164 | 165 | // Keeps common configuration settings 166 | struct Config { 167 | unsigned int robustnessValue; 168 | unsigned int queryInterval; 169 | unsigned int queryResponseInterval; 170 | // Used on startup.. 171 | unsigned int startupQueryInterval; 172 | unsigned int startupQueryCount; 173 | // Last member probe... 174 | unsigned int lastMemberQueryInterval; 175 | unsigned int lastMemberQueryCount; 176 | // Set if upstream leave messages should be sent instantly.. 177 | unsigned short fastUpstreamLeave; 178 | // Size in bytes of hash table of downstream hosts used for fast leave 179 | unsigned int downstreamHostsHashTableSize; 180 | //~ aimwang added 181 | // Set if nneed to detect new interface. 182 | unsigned short rescanVif; 183 | // Set if not detect new interface for down stream. 184 | unsigned short defaultInterfaceState; // 0: disable, 2: downstream 185 | //~ aimwang added done 186 | char chroot[PATH_MAX]; 187 | char user[LOGIN_NAME_MAX]; 188 | }; 189 | 190 | // Holds the indeces of the upstream IF... 191 | extern int upStreamIfIdx[MAX_UPS_VIFS]; 192 | 193 | /* ifvc.c 194 | */ 195 | void rebuildIfVc( void ); 196 | void buildIfVc( void ); 197 | struct IfDesc *getIfByName( const char *IfName ); 198 | struct IfDesc *getIfByIx( unsigned Ix ); 199 | struct IfDesc *getIfByAddress( uint32_t Ix ); 200 | struct IfDesc *getIfByVifIndex( unsigned vifindex ); 201 | int isAdressValidForIf(struct IfDesc* intrface, uint32_t ipaddr); 202 | 203 | /* mroute-api.c 204 | */ 205 | struct MRouteDesc { 206 | struct in_addr OriginAdr, McAdr; 207 | short InVif; 208 | uint8_t TtlVc[MAXVIFS]; 209 | }; 210 | 211 | // IGMP socket as interface for the mrouted API 212 | // - receives the IGMP messages 213 | extern int MRouterFD; 214 | 215 | int enableMRouter( void ); 216 | void disableMRouter( void ); 217 | void addVIF( struct IfDesc *Dp ); 218 | void delVIF( struct IfDesc *Dp ); 219 | int addMRoute( struct MRouteDesc * Dp ); 220 | int delMRoute( struct MRouteDesc * Dp ); 221 | int getVifIx( struct IfDesc *IfDp ); 222 | 223 | /* config.c 224 | */ 225 | int loadConfig(char *configFile); 226 | void configureVifs(void); 227 | struct Config *getCommonConfig(void); 228 | 229 | /* igmp.c 230 | */ 231 | extern uint32_t allhosts_group; 232 | extern uint32_t allrouters_group; 233 | extern uint32_t alligmp3_group; 234 | void initIgmp(void); 235 | void acceptIgmp(int); 236 | void sendIgmp (uint32_t, uint32_t, int, int, uint32_t, int, int); 237 | 238 | /* lib.c 239 | */ 240 | char *fmtInAdr( char *St, struct in_addr InAdr ); 241 | char *inetFmt(uint32_t addr, char *s); 242 | char *inetFmts(uint32_t addr, uint32_t mask, char *s); 243 | uint16_t inetChksum(uint16_t *addr, int len); 244 | 245 | /* kern.c 246 | */ 247 | void k_set_rcvbuf(int bufsize, int minsize); 248 | void k_hdr_include(int hdrincl); 249 | void k_set_ttl(int t); 250 | void k_set_loop(int l); 251 | void k_set_if(uint32_t ifa, int ifidx); 252 | void k_join(struct IfDesc *ifd, uint32_t grp); 253 | void k_leave(struct IfDesc *ifd, uint32_t grp); 254 | 255 | /* rttable.c 256 | */ 257 | void initRouteTable(void); 258 | void clearAllRoutes(void); 259 | int insertRoute(uint32_t group, int ifx, uint32_t src); 260 | int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif); 261 | void ageActiveRoutes(void); 262 | void setRouteLastMemberMode(uint32_t group, uint32_t src); 263 | int lastMemberGroupAge(uint32_t group); 264 | int interfaceInRoute(int32_t group, int Ix); 265 | 266 | /* request.c 267 | */ 268 | void acceptGroupReport(uint32_t src, uint32_t group); 269 | void acceptLeaveMessage(uint32_t src, uint32_t group); 270 | void sendGeneralMembershipQuery(void); 271 | 272 | /* callout.c 273 | */ 274 | typedef void (*timer_f)(void *); 275 | 276 | void callout_init(void); 277 | void free_all_callouts(void); 278 | void age_callout_queue(int); 279 | int timer_nextTimer(void); 280 | int timer_setTimer(int, timer_f, void *); 281 | int timer_clearTimer(int); 282 | int timer_leftTimer(int); 283 | 284 | /* confread.c 285 | */ 286 | #define MAX_TOKEN_LENGTH 30 287 | 288 | int openConfigFile(char *filename); 289 | void closeConfigFile(void); 290 | char* nextConfigToken(void); 291 | char* getCurrentConfigToken(void); 292 | -------------------------------------------------------------------------------- /src/igmpv3.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | */ 20 | /** 21 | * igmpv3.h - Header file for common IGMPv3 includes. 22 | */ 23 | 24 | struct igmpv3_grec { 25 | u_int8_t grec_type; 26 | u_int8_t grec_auxwords; 27 | u_int16_t grec_nsrcs; 28 | struct in_addr grec_mca; 29 | struct in_addr grec_src[0]; 30 | }; 31 | 32 | struct igmpv3_report { 33 | u_int8_t igmp_type; 34 | u_int8_t igmp_resv1; 35 | u_int16_t igmp_cksum; 36 | u_int16_t igmp_resv2; 37 | u_int16_t igmp_ngrec; 38 | struct igmpv3_grec igmp_grec[0]; 39 | }; 40 | 41 | #define IGMPV3_MODE_IS_INCLUDE 1 42 | #define IGMPV3_MODE_IS_EXCLUDE 2 43 | #define IGMPV3_CHANGE_TO_INCLUDE 3 44 | #define IGMPV3_CHANGE_TO_EXCLUDE 4 45 | #define IGMPV3_ALLOW_NEW_SOURCES 5 46 | #define IGMPV3_BLOCK_OLD_SOURCES 6 47 | 48 | #define IGMPV3_MINLEN 12 49 | -------------------------------------------------------------------------------- /src/kern.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | 35 | 36 | #include "igmpproxy.h" 37 | 38 | int curttl = 0; 39 | 40 | void k_set_rcvbuf(int bufsize, int minsize) { 41 | int delta = bufsize / 2; 42 | int iter = 0; 43 | 44 | /* 45 | * Set the socket buffer. If we can't set it as large as we 46 | * want, search around to try to find the highest acceptable 47 | * value. The highest acceptable value being smaller than 48 | * minsize is a fatal error. 49 | */ 50 | if (setsockopt(MRouterFD, SOL_SOCKET, SO_RCVBUF, 51 | (char *)&bufsize, sizeof(bufsize)) < 0) { 52 | bufsize -= delta; 53 | while (1) { 54 | iter++; 55 | if (delta > 1) 56 | delta /= 2; 57 | 58 | if (setsockopt(MRouterFD, SOL_SOCKET, SO_RCVBUF, 59 | (char *)&bufsize, sizeof(bufsize)) < 0) { 60 | bufsize -= delta; 61 | } else { 62 | if (delta < 1024) 63 | break; 64 | bufsize += delta; 65 | } 66 | } 67 | if (bufsize < minsize) { 68 | my_log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u", 69 | bufsize, minsize); 70 | /*NOTREACHED*/ 71 | } 72 | } 73 | my_log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations", bufsize, iter); 74 | } 75 | 76 | void k_hdr_include(int hdrincl) { 77 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_HDRINCL, 78 | (char *)&hdrincl, sizeof(hdrincl)) < 0) 79 | my_log(LOG_WARNING, errno, "setsockopt IP_HDRINCL %u", hdrincl); 80 | } 81 | 82 | 83 | void k_set_ttl(int t) { 84 | #ifndef RAW_OUTPUT_IS_RAW 85 | unsigned char ttl; 86 | 87 | ttl = t; 88 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_MULTICAST_TTL, 89 | (char *)&ttl, sizeof(ttl)) < 0) 90 | my_log(LOG_WARNING, errno, "setsockopt IP_MULTICAST_TTL %u", ttl); 91 | #endif 92 | curttl = t; 93 | } 94 | 95 | void k_set_loop(int l) { 96 | unsigned char loop; 97 | 98 | loop = l; 99 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_MULTICAST_LOOP, 100 | (char *)&loop, sizeof(loop)) < 0) 101 | my_log(LOG_WARNING, errno, "setsockopt IP_MULTICAST_LOOP %u", loop); 102 | } 103 | void k_set_if(uint32_t ifa, int ifidx) { 104 | #ifdef HAVE_STRUCT_IP_MREQN 105 | struct ip_mreqn ifsel; 106 | ifsel.imr_address.s_addr = ifa; 107 | ifsel.imr_ifindex = ifidx; 108 | #else 109 | struct in_addr ifsel; 110 | ifsel.s_addr = ifa; 111 | #endif 112 | 113 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_MULTICAST_IF, 114 | (char *)&ifsel, sizeof(ifsel)) < 0) 115 | my_log(LOG_WARNING, errno, "setsockopt IP_MULTICAST_IF %s", 116 | inetFmt(ifa, s1)); 117 | } 118 | 119 | void k_join(struct IfDesc *ifd, uint32_t grp) { 120 | #ifdef HAVE_STRUCT_IP_MREQN 121 | struct ip_mreqn mreq; 122 | mreq.imr_address.s_addr = ifd->InAdr.s_addr; 123 | mreq.imr_ifindex = ifd->ifIndex; 124 | #else 125 | struct ip_mreq mreq; 126 | mreq.imr_interface.s_addr = ifd->InAdr.s_addr; 127 | #endif 128 | mreq.imr_multiaddr.s_addr = grp; 129 | 130 | my_log(LOG_NOTICE, 0, "Joining group %s on interface %s", inetFmt(grp, s1), ifd->Name); 131 | 132 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, 133 | (char *)&mreq, sizeof(mreq)) < 0) { 134 | int mcastGroupExceeded = (errno == ENOBUFS); 135 | my_log(LOG_WARNING, errno, "can't join group %s on interface %s", 136 | inetFmt(grp, s1), ifd->Name); 137 | if (mcastGroupExceeded) { 138 | my_log(LOG_WARNING, 0, "Maximum number of multicast groups were exceeded"); 139 | #ifdef __linux__ 140 | my_log(LOG_WARNING, 0, "Check settings of '/sbin/sysctl net.ipv4.igmp_max_memberships'"); 141 | #endif 142 | } 143 | } 144 | } 145 | 146 | void k_leave(struct IfDesc *ifd, uint32_t grp) { 147 | #ifdef HAVE_STRUCT_IP_MREQN 148 | struct ip_mreqn mreq; 149 | mreq.imr_address.s_addr = ifd->InAdr.s_addr; 150 | mreq.imr_ifindex = ifd->ifIndex; 151 | #else 152 | struct ip_mreq mreq; 153 | mreq.imr_interface.s_addr = ifd->InAdr.s_addr; 154 | #endif 155 | mreq.imr_multiaddr.s_addr = grp; 156 | 157 | my_log(LOG_NOTICE, 0, "Leaving group %s on interface %s", inetFmt(grp, s1), ifd->Name); 158 | 159 | if (setsockopt(MRouterFD, IPPROTO_IP, IP_DROP_MEMBERSHIP, 160 | (char *)&mreq, sizeof(mreq)) < 0) 161 | my_log(LOG_WARNING, errno, "can't leave group %s on interface %s", 162 | inetFmt(grp, s1), ifd->Name); 163 | } 164 | -------------------------------------------------------------------------------- /src/lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | 35 | #include "igmpproxy.h" 36 | 37 | /* 38 | * Exported variables. 39 | */ 40 | char s1[19]; /* buffers to hold the string representations */ 41 | char s2[19]; /* of IP addresses, to be passed to inet_fmt() */ 42 | char s3[19]; /* or inet_fmts(). */ 43 | char s4[19]; 44 | 45 | /* 46 | ** Formats 'InAdr' into a dotted decimal string. 47 | ** 48 | ** returns: - pointer to 'St' 49 | ** 50 | */ 51 | char *fmtInAdr( char *St, struct in_addr InAdr ) { 52 | sprintf( St, "%u.%u.%u.%u", 53 | ((uint8_t *)&InAdr.s_addr)[ 0 ], 54 | ((uint8_t *)&InAdr.s_addr)[ 1 ], 55 | ((uint8_t *)&InAdr.s_addr)[ 2 ], 56 | ((uint8_t *)&InAdr.s_addr)[ 3 ] ); 57 | 58 | return St; 59 | } 60 | 61 | /* 62 | * Convert an IP address in u_long (network) format into a printable string. 63 | */ 64 | char *inetFmt(uint32_t addr, char *s) { 65 | register unsigned char *a; 66 | 67 | a = (unsigned char *)&addr; 68 | sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); 69 | return(s); 70 | } 71 | 72 | /* 73 | * Convert an IP subnet number in u_long (network) format into a printable 74 | * string including the netmask as a number of bits. 75 | */ 76 | char *inetFmts(uint32_t addr, uint32_t mask, char *s) { 77 | register unsigned char *a, *m; 78 | int bits; 79 | 80 | if ((addr == 0) && (mask == 0)) { 81 | sprintf(s, "default"); 82 | return(s); 83 | } 84 | a = (unsigned char *)&addr; 85 | m = (unsigned char *)&mask; 86 | bits = 33 - ffs(ntohl(mask)); 87 | 88 | if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3], 89 | bits); 90 | else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits); 91 | else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits); 92 | else sprintf(s, "%u/%d", a[0], bits); 93 | 94 | return(s); 95 | } 96 | 97 | /* 98 | * inet_cksum extracted from: 99 | * P I N G . C 100 | * 101 | * Author - 102 | * Mike Muuss 103 | * U. S. Army Ballistic Research Laboratory 104 | * December, 1983 105 | * Modified at Uc Berkeley 106 | * 107 | * (ping.c) Status - 108 | * Public Domain. Distribution Unlimited. 109 | * 110 | * I N _ C K S U M 111 | * 112 | * Checksum routine for Internet Protocol family headers (C Version) 113 | * 114 | */ 115 | uint16_t inetChksum(uint16_t *addr, int len) { 116 | register int nleft = len; 117 | register uint16_t *w = addr; 118 | uint16_t answer = 0; 119 | register int32_t sum = 0; 120 | 121 | /* 122 | * Our algorithm is simple, using a 32 bit accumulator (sum), 123 | * we add sequential 16 bit words to it, and at the end, fold 124 | * back all the carry bits from the top 16 bits into the lower 125 | * 16 bits. 126 | */ 127 | while (nleft > 1) { 128 | sum += *w++; 129 | nleft -= 2; 130 | } 131 | 132 | /* mop up an odd byte, if necessary */ 133 | if (nleft == 1) { 134 | *(uint8_t *) (&answer) = *(uint8_t *)w ; 135 | sum += answer; 136 | } 137 | 138 | /* 139 | * add back carry outs from top 16 bits to low 16 bits 140 | */ 141 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 142 | sum += (sum >> 16); /* add carry */ 143 | answer = ~sum; /* truncate to 16 bits */ 144 | return(answer); 145 | } 146 | -------------------------------------------------------------------------------- /src/mroute-api.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * mroute-api.c 36 | * 37 | * This module contains the interface routines to the Linux mrouted API 38 | */ 39 | 40 | 41 | #include "igmpproxy.h" 42 | 43 | // need an IGMP socket as interface for the mrouted API 44 | // - receives the IGMP messages 45 | int MRouterFD = -1; /* socket for all network I/O */ 46 | char *recv_buf; /* input packet buffer */ 47 | char *send_buf; /* output packet buffer */ 48 | 49 | 50 | // my internal virtual interfaces descriptor vector 51 | static struct VifDesc { 52 | struct IfDesc *IfDp; 53 | } VifDescVc[ MAXVIFS ]; 54 | 55 | /* 56 | ** Initialises the mrouted API and locks it by this exclusively. 57 | ** 58 | ** returns: - 0 if the functions succeeds 59 | ** - the errno value for non-fatal failure condition 60 | */ 61 | int enableMRouter(void) 62 | { 63 | int Va = 1; 64 | 65 | if ( (MRouterFD = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0 ) 66 | my_log( LOG_ERR, errno, "IGMP socket open" ); 67 | 68 | if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_INIT, 69 | (void *)&Va, sizeof( Va ) ) ) 70 | return errno; 71 | 72 | return 0; 73 | } 74 | 75 | /* 76 | ** Diable the mrouted API and relases by this the lock. 77 | ** 78 | */ 79 | void disableMRouter(void) 80 | { 81 | if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_DONE, NULL, 0 ) < 0 ) 82 | my_log( LOG_WARNING, errno, "MRT_DONE" ); 83 | 84 | if ( close( MRouterFD ) < 0 ) 85 | my_log( LOG_WARNING, errno, "IGMP socket close" ); 86 | 87 | MRouterFD = -1; 88 | } 89 | 90 | /* 91 | * aimwang: delVIF() 92 | */ 93 | void delVIF( struct IfDesc *IfDp ) 94 | { 95 | struct vifctl VifCtl; 96 | 97 | if ((unsigned int)-1 == IfDp->index) 98 | return; 99 | 100 | VifCtl.vifc_vifi = IfDp->index; 101 | 102 | my_log( LOG_NOTICE, 0, "removing VIF, Ix %d Fl 0x%x IP 0x%08x %s, Threshold: %d, Ratelimit: %d", 103 | IfDp->index, IfDp->Flags, IfDp->InAdr.s_addr, IfDp->Name, IfDp->threshold, IfDp->ratelimit); 104 | 105 | if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_DEL_VIF, 106 | (char *)&VifCtl, sizeof( VifCtl ) ) ) 107 | my_log( LOG_WARNING, errno, "MRT_DEL_VIF" ); 108 | } 109 | 110 | /* 111 | ** Adds the interface '*IfDp' as virtual interface to the mrouted API 112 | ** 113 | */ 114 | void addVIF( struct IfDesc *IfDp ) 115 | { 116 | struct vifctl VifCtl; 117 | struct VifDesc *VifDp; 118 | 119 | /* search free (aimwang: or exist) VifDesc 120 | */ 121 | for ( VifDp = VifDescVc; VifDp < VCEP( VifDescVc ); VifDp++ ) { 122 | if ( ! VifDp->IfDp || VifDp->IfDp == IfDp) 123 | break; 124 | } 125 | 126 | /* no more space 127 | */ 128 | if ( VifDp >= VCEP( VifDescVc ) ) 129 | my_log( LOG_ERR, ENOMEM, "addVIF, out of VIF space" ); 130 | 131 | VifDp->IfDp = IfDp; 132 | 133 | VifCtl.vifc_vifi = VifDp - VifDescVc; 134 | VifCtl.vifc_threshold = VifDp->IfDp->threshold; // Packet TTL must be at least 1 to pass them 135 | VifCtl.vifc_rate_limit = VifDp->IfDp->ratelimit; // Ratelimit 136 | 137 | #ifdef VIFF_USE_IFINDEX 138 | VifCtl.vifc_flags = VIFF_USE_IFINDEX; 139 | VifCtl.vifc_lcl_ifindex = VifDp->IfDp->ifIndex; 140 | #else 141 | VifCtl.vifc_flags = 0; 142 | VifCtl.vifc_lcl_addr.s_addr = VifDp->IfDp->InAdr.s_addr; 143 | #endif 144 | 145 | VifCtl.vifc_rmt_addr.s_addr = INADDR_ANY; 146 | 147 | // Set the index... 148 | VifDp->IfDp->index = VifCtl.vifc_vifi; 149 | 150 | my_log( LOG_NOTICE, 0, "adding VIF, Ix %d Fl 0x%x IP 0x%08x %s, Threshold: %d, Ratelimit: %d", 151 | VifCtl.vifc_vifi, VifCtl.vifc_flags, VifCtl.vifc_lcl_addr.s_addr, VifDp->IfDp->Name, 152 | VifCtl.vifc_threshold, VifCtl.vifc_rate_limit); 153 | 154 | struct SubnetList *currSubnet; 155 | for(currSubnet = IfDp->allowednets; currSubnet; currSubnet = currSubnet->next) { 156 | my_log(LOG_DEBUG, 0, " Network for [%s] : %s", 157 | IfDp->Name, 158 | inetFmts(currSubnet->subnet_addr, currSubnet->subnet_mask, s1)); 159 | } 160 | 161 | if ( setsockopt( MRouterFD, IPPROTO_IP, MRT_ADD_VIF, 162 | (char *)&VifCtl, sizeof( VifCtl ) ) ) 163 | my_log( LOG_ERR, errno, "MRT_ADD_VIF" ); 164 | 165 | } 166 | 167 | /* 168 | ** Adds the multicast routed '*Dp' to the kernel routes 169 | ** 170 | ** returns: - 0 if the function succeeds 171 | ** - the errno value for non-fatal failure condition 172 | */ 173 | int addMRoute( struct MRouteDesc *Dp ) 174 | { 175 | struct mfcctl CtlReq; 176 | int rc; 177 | 178 | CtlReq.mfcc_origin = Dp->OriginAdr; 179 | CtlReq.mfcc_mcastgrp = Dp->McAdr; 180 | CtlReq.mfcc_parent = Dp->InVif; 181 | 182 | /* copy the TTL vector 183 | */ 184 | 185 | memcpy( CtlReq.mfcc_ttls, Dp->TtlVc, sizeof( CtlReq.mfcc_ttls ) ); 186 | 187 | { 188 | char FmtBuO[ 32 ], FmtBuM[ 32 ]; 189 | 190 | my_log( LOG_NOTICE, 0, "Adding MFC: %s -> %s, InpVIf: %d", 191 | fmtInAdr( FmtBuO, CtlReq.mfcc_origin ), 192 | fmtInAdr( FmtBuM, CtlReq.mfcc_mcastgrp ), 193 | (int)CtlReq.mfcc_parent 194 | ); 195 | } 196 | 197 | rc = setsockopt( MRouterFD, IPPROTO_IP, MRT_ADD_MFC, 198 | (void *)&CtlReq, sizeof( CtlReq ) ); 199 | if (rc) 200 | my_log( LOG_WARNING, errno, "MRT_ADD_MFC" ); 201 | 202 | return rc; 203 | } 204 | 205 | /* 206 | ** Removes the multicast routed '*Dp' from the kernel routes 207 | ** 208 | ** returns: - 0 if the function succeeds 209 | ** - the errno value for non-fatal failure condition 210 | */ 211 | int delMRoute( struct MRouteDesc *Dp ) 212 | { 213 | struct mfcctl CtlReq; 214 | int rc; 215 | 216 | CtlReq.mfcc_origin = Dp->OriginAdr; 217 | CtlReq.mfcc_mcastgrp = Dp->McAdr; 218 | CtlReq.mfcc_parent = Dp->InVif; 219 | 220 | /* clear the TTL vector 221 | */ 222 | memset( CtlReq.mfcc_ttls, 0, sizeof( CtlReq.mfcc_ttls ) ); 223 | 224 | { 225 | char FmtBuO[ 32 ], FmtBuM[ 32 ]; 226 | 227 | my_log( LOG_NOTICE, 0, "Removing MFC: %s -> %s, InpVIf: %d", 228 | fmtInAdr( FmtBuO, CtlReq.mfcc_origin ), 229 | fmtInAdr( FmtBuM, CtlReq.mfcc_mcastgrp ), 230 | (int)CtlReq.mfcc_parent 231 | ); 232 | } 233 | 234 | rc = setsockopt( MRouterFD, IPPROTO_IP, MRT_DEL_MFC, 235 | (void *)&CtlReq, sizeof( CtlReq ) ); 236 | if (rc) 237 | my_log( LOG_WARNING, errno, "MRT_DEL_MFC" ); 238 | 239 | return rc; 240 | } 241 | 242 | /* 243 | ** Returns for the virtual interface index for '*IfDp' 244 | ** 245 | ** returns: - the vitrual interface index if the interface is registered 246 | ** - -1 if no virtual interface exists for the interface 247 | ** 248 | */ 249 | int getVifIx( struct IfDesc *IfDp ) 250 | { 251 | struct VifDesc *Dp; 252 | 253 | for ( Dp = VifDescVc; Dp < VCEP( VifDescVc ); Dp++ ) 254 | if ( Dp->IfDp == IfDp ) 255 | return Dp - VifDescVc; 256 | 257 | return -1; 258 | } 259 | -------------------------------------------------------------------------------- /src/os-dragonfly.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define IGMP_V3_MEMBERSHIP_REPORT 0x22 7 | 8 | #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) 9 | 10 | static inline unsigned short ip_data_len(const struct ip *ip) 11 | { 12 | return ip->ip_len; 13 | } 14 | 15 | static inline void ip_set_len(struct ip *ip, unsigned short len) 16 | { 17 | ip->ip_len = len; 18 | } 19 | -------------------------------------------------------------------------------- /src/os-freebsd.h: -------------------------------------------------------------------------------- 1 | #define __BSD_VISIBLE 1 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if __FreeBSD_version >= 800069 && defined BURN_BRIDGES \ 12 | || __FreeBSD_version >= 800098 13 | #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY 14 | #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT 15 | #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT 16 | #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE 17 | #endif 18 | #define IGMP_V3_MEMBERSHIP_REPORT 0x22 19 | 20 | #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) 21 | 22 | static inline unsigned short ip_data_len(const struct ip *ip) 23 | { 24 | #if __FreeBSD_version >= 1100030 25 | return ntohs(ip->ip_len) - (ip->ip_hl << 2); 26 | #elif __FreeBSD_version >= 900044 27 | return ip->ip_len - (ip->ip_hl << 2); 28 | #else 29 | return ip->ip_len; 30 | #endif 31 | } 32 | 33 | static inline void ip_set_len(struct ip *ip, unsigned short len) 34 | { 35 | #if __FreeBSD_version >= 1100030 36 | ip->ip_len = htons(len); 37 | #else 38 | ip->ip_len = len; 39 | #endif 40 | } 41 | -------------------------------------------------------------------------------- /src/os-linux.h: -------------------------------------------------------------------------------- 1 | #define _LINUX_IN_H 2 | #define _GNU_SOURCE 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define IGMP_V3_MEMBERSHIP_REPORT 0x22 11 | 12 | #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) 13 | 14 | static inline unsigned short ip_data_len(const struct ip *ip) 15 | { 16 | return ntohs(ip->ip_len) - (ip->ip_hl << 2); 17 | } 18 | 19 | static inline void ip_set_len(struct ip *ip, unsigned short len) 20 | { 21 | ip->ip_len = htons(len); 22 | } 23 | -------------------------------------------------------------------------------- /src/os-netbsd.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY 7 | #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT 8 | #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT 9 | #define IGMP_V3_MEMBERSHIP_REPORT 0x22 10 | #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE 11 | 12 | #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) 13 | 14 | static inline unsigned short ip_data_len(const struct ip *ip) 15 | { 16 | return ip->ip_len; 17 | } 18 | 19 | static inline void ip_set_len(struct ip *ip, unsigned short len) 20 | { 21 | ip->ip_len = len; 22 | } 23 | -------------------------------------------------------------------------------- /src/os-openbsd.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY 7 | #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT 8 | #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT 9 | #define IGMP_V3_MEMBERSHIP_REPORT 0x22 10 | #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE 11 | 12 | #define INADDR_ALLRTRS_GROUP INADDR_ALLROUTERS_GROUP 13 | #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) 14 | 15 | static inline unsigned short ip_data_len(const struct ip *ip) 16 | { 17 | return ntohs(ip->ip_len) - (ip->ip_hl << 2); 18 | } 19 | 20 | static inline void ip_set_len(struct ip *ip, unsigned short len) 21 | { 22 | ip->ip_len = htons(len); 23 | } 24 | -------------------------------------------------------------------------------- /src/os-qnxnto.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY 7 | #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT 8 | #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT 9 | #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE 10 | 11 | #define IPOPT_RA 148 /* router alert */ 12 | 13 | static inline u_short ip_data_len(const struct ip *ip) 14 | { 15 | return ip->ip_len; 16 | } 17 | 18 | static inline void ip_set_len(struct ip *ip, u_short len) 19 | { 20 | ip->ip_len = len; 21 | } 22 | -------------------------------------------------------------------------------- /src/request.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * request.c 36 | * 37 | * Functions for recieveing and processing IGMP requests. 38 | * 39 | */ 40 | 41 | #include "igmpproxy.h" 42 | 43 | // Prototypes... 44 | void sendGroupSpecificMemberQuery(void *argument); 45 | 46 | typedef struct { 47 | uint32_t group; 48 | // uint32_t vifAddr; 49 | short started; 50 | } GroupVifDesc; 51 | 52 | 53 | /** 54 | * Handles incoming membership reports, and 55 | * appends them to the routing table. 56 | */ 57 | void acceptGroupReport(uint32_t src, uint32_t group) { 58 | struct IfDesc *sourceVif; 59 | 60 | // Sanitycheck the group adress... 61 | if(!IN_MULTICAST( ntohl(group) )) { 62 | my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.", 63 | inetFmt(group, s1)); 64 | return; 65 | } 66 | 67 | // Find the interface on which the report was received. 68 | sourceVif = getIfByAddress( src ); 69 | if(sourceVif == NULL) { 70 | my_log(LOG_WARNING, 0, "No interfaces found for source %s", 71 | inetFmt(src,s1)); 72 | return; 73 | } 74 | 75 | if(sourceVif->InAdr.s_addr == src) { 76 | my_log(LOG_NOTICE, 0, "The IGMP message was from myself. Ignoring."); 77 | return; 78 | } 79 | 80 | // We have a IF so check that it's an downstream IF. 81 | if(sourceVif->state == IF_STATE_DOWNSTREAM) { 82 | 83 | my_log(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d", 84 | inetFmt(group,s1), inetFmt(src,s2), sourceVif->index); 85 | 86 | // If we don't have a black- and whitelist we insertRoute and done 87 | if(sourceVif->allowedgroups == NULL) 88 | { 89 | insertRoute(group, sourceVif->index, src); 90 | return; 91 | } 92 | 93 | // Check if this Request is legit on this interface 94 | bool allow_list = false; 95 | struct SubnetList *match = NULL; 96 | struct SubnetList *sn; 97 | 98 | for(sn = sourceVif->allowedgroups; sn != NULL; sn = sn->next) { 99 | // Check if there is a whitelist 100 | if (sn->allow) 101 | allow_list = true; 102 | if((group & sn->subnet_mask) == sn->subnet_addr) 103 | match = sn; 104 | } 105 | 106 | if((!allow_list && match == NULL) || 107 | (allow_list && match != NULL && match->allow)) { 108 | // The membership report was OK... Insert it into the route table.. 109 | insertRoute(group, sourceVif->index, src); 110 | return; 111 | } 112 | my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); 113 | } else { 114 | // Log the state of the interface the report was received on. 115 | my_log(LOG_INFO, 0, "Mebership report was received on %s. Ignoring.", 116 | sourceVif->state==IF_STATE_UPSTREAM?"the upstream interface":"a disabled interface"); 117 | } 118 | } 119 | 120 | /** 121 | * Recieves and handles a group leave message. 122 | */ 123 | void acceptLeaveMessage(uint32_t src, uint32_t group) { 124 | struct IfDesc *sourceVif; 125 | 126 | my_log(LOG_DEBUG, 0, 127 | "Got leave message from %s to %s. Starting last member detection.", 128 | inetFmt(src, s1), inetFmt(group, s2)); 129 | 130 | // Sanitycheck the group adress... 131 | if(!IN_MULTICAST( ntohl(group) )) { 132 | my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group.", 133 | inetFmt(group, s1)); 134 | return; 135 | } 136 | 137 | // Find the interface on which the report was received. 138 | sourceVif = getIfByAddress( src ); 139 | if(sourceVif == NULL) { 140 | my_log(LOG_WARNING, 0, "No interfaces found for source %s", 141 | inetFmt(src,s1)); 142 | return; 143 | } 144 | 145 | // We have a IF so check that it's an downstream IF. 146 | if(sourceVif->state == IF_STATE_DOWNSTREAM) { 147 | 148 | GroupVifDesc *gvDesc; 149 | gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc)); 150 | 151 | // Tell the route table that we are checking for remaining members... 152 | setRouteLastMemberMode(group, src); 153 | 154 | // Call the group spesific membership querier... 155 | gvDesc->group = group; 156 | // gvDesc->vifAddr = sourceVif->InAdr.s_addr; 157 | gvDesc->started = 0; 158 | 159 | sendGroupSpecificMemberQuery(gvDesc); 160 | 161 | } else { 162 | // just ignore the leave request... 163 | my_log(LOG_DEBUG, 0, "The found if for %s was not downstream. Ignoring leave request.", inetFmt(src, s1)); 164 | } 165 | } 166 | 167 | /** 168 | * Sends a group specific member report query until the 169 | * group times out... 170 | */ 171 | void sendGroupSpecificMemberQuery(void *argument) { 172 | struct Config *conf = getCommonConfig(); 173 | struct IfDesc *Dp; 174 | int Ix; 175 | 176 | // Cast argument to correct type... 177 | GroupVifDesc *gvDesc = (GroupVifDesc*) argument; 178 | 179 | if(gvDesc->started) { 180 | // If aging returns false, we don't do any further action... 181 | if(!lastMemberGroupAge(gvDesc->group)) { 182 | // FIXME: Should we free gvDesc here? 183 | return; 184 | } 185 | } else { 186 | gvDesc->started = 1; 187 | } 188 | 189 | /** 190 | * FIXME: This loops through all interfaces the group is active on an sends queries. 191 | * It might be better to send only a query on the interface the leave was accepted on and remove only that interface from the route. 192 | */ 193 | 194 | // Loop through all downstream interfaces 195 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 196 | if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 197 | if(Dp->state == IF_STATE_DOWNSTREAM) { 198 | // Is that interface used in the group? 199 | if (interfaceInRoute(gvDesc->group ,Dp->index)) { 200 | 201 | // Send a group specific membership query... 202 | sendIgmp(Dp->InAdr.s_addr, gvDesc->group, 203 | IGMP_MEMBERSHIP_QUERY, 204 | conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, 205 | gvDesc->group, 0, Dp->ifIndex); 206 | 207 | my_log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", 208 | inetFmt(Dp->InAdr.s_addr,s1), inetFmt(gvDesc->group,s2), 209 | conf->lastMemberQueryInterval); 210 | } 211 | } 212 | } 213 | } 214 | 215 | // Set timeout for next round... 216 | timer_setTimer(conf->lastMemberQueryInterval, sendGroupSpecificMemberQuery, gvDesc); 217 | } 218 | 219 | 220 | /** 221 | * Sends a general membership query on downstream VIFs 222 | */ 223 | void sendGeneralMembershipQuery(void) { 224 | struct Config *conf = getCommonConfig(); 225 | struct IfDesc *Dp; 226 | int Ix; 227 | 228 | // Loop through all downstream vifs... 229 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 230 | if ( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) ) { 231 | if(Dp->state == IF_STATE_DOWNSTREAM) { 232 | // Send the membership query... 233 | sendIgmp(Dp->InAdr.s_addr, allhosts_group, 234 | IGMP_MEMBERSHIP_QUERY, 235 | conf->queryResponseInterval * IGMP_TIMER_SCALE, 0, 0, Dp->ifIndex); 236 | 237 | my_log(LOG_DEBUG, 0, 238 | "Sent membership query from %s ifIndex %d to %s. Delay: %d", 239 | inetFmt(Dp->InAdr.s_addr,s1), 240 | Dp->ifIndex, 241 | inetFmt(allhosts_group,s2), 242 | conf->queryResponseInterval); 243 | } 244 | } 245 | } 246 | 247 | // Install timer for aging active routes. 248 | timer_setTimer(conf->queryResponseInterval, (timer_f)ageActiveRoutes, NULL); 249 | 250 | // Install timer for next general query... 251 | if(conf->startupQueryCount>0) { 252 | // Use quick timer... 253 | timer_setTimer(conf->startupQueryInterval, (timer_f)sendGeneralMembershipQuery, NULL); 254 | // Decrease startup counter... 255 | conf->startupQueryCount--; 256 | } 257 | else { 258 | // Use slow timer... 259 | timer_setTimer(conf->queryInterval, (timer_f)sendGeneralMembershipQuery, NULL); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/rttable.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | /** 35 | * rttable.c 36 | * 37 | * Updates the routingtable according to 38 | * received request. 39 | */ 40 | 41 | #include "igmpproxy.h" 42 | 43 | #define MAX_ORIGINS 4 44 | 45 | /** 46 | * Routing table structure definition. Double linked list... 47 | */ 48 | struct RouteTable { 49 | struct RouteTable *nextroute; // Pointer to the next group in line. 50 | struct RouteTable *prevroute; // Pointer to the previous group in line. 51 | uint32_t group; // The group to route 52 | uint32_t originAddrs[MAX_ORIGINS]; // The origin adresses (only set on activated routes) 53 | uint32_t vifBits; // Bits representing recieving VIFs. 54 | 55 | // Keeps the upstream membership state... 56 | short upstrState; // Upstream membership state. 57 | int upstrVif; // Upstream Vif Index. 58 | 59 | // These parameters contain aging details. 60 | uint32_t ageVifBits; // Bits representing aging VIFs. 61 | int ageValue; // Downcounter for death. 62 | int ageActivity; // Records any acitivity that notes there are still listeners. 63 | 64 | // Keeps downstream hosts information 65 | uint32_t downstreamHostsHashSeed; 66 | uint8_t downstreamHostsHashTable[]; 67 | }; 68 | 69 | 70 | // Keeper for the routing table... 71 | static struct RouteTable *routing_table; 72 | 73 | // Prototypes 74 | void logRouteTable(const char *header); 75 | int internAgeRoute(struct RouteTable *croute); 76 | int internUpdateKernelRoute(struct RouteTable *route, int activate); 77 | 78 | 79 | /** 80 | * Functions for downstream hosts hash table 81 | */ 82 | 83 | // MurmurHash3 32bit hash function by Austin Appleby, public domain 84 | static uint32_t murmurhash3(uint32_t x) { 85 | x ^= x >> 16; 86 | x *= 0x85ebca6b; 87 | x ^= x >> 13; 88 | x *= 0xc2b2ae35; 89 | x ^= x >> 16; 90 | return x; 91 | } 92 | 93 | static inline void setDownstreamHost(struct Config *conf, struct RouteTable *croute, uint32_t src) { 94 | uint32_t hash = murmurhash3(src ^ croute->downstreamHostsHashSeed) % (conf->downstreamHostsHashTableSize*8); 95 | BIT_SET(croute->downstreamHostsHashTable[hash/8], hash%8); 96 | } 97 | 98 | static inline void clearDownstreamHost(struct Config *conf, struct RouteTable *croute, uint32_t src) { 99 | uint32_t hash = murmurhash3(src ^ croute->downstreamHostsHashSeed) % (conf->downstreamHostsHashTableSize*8); 100 | BIT_CLR(croute->downstreamHostsHashTable[hash/8], hash%8); 101 | } 102 | 103 | static inline void zeroDownstreamHosts(struct Config *conf, struct RouteTable *croute) { 104 | croute->downstreamHostsHashSeed = ((uint32_t)rand() << 16) | (uint32_t)rand(); 105 | memset(croute->downstreamHostsHashTable, 0, conf->downstreamHostsHashTableSize); 106 | } 107 | 108 | static inline int testNoDownstreamHost(struct Config *conf, struct RouteTable *croute) { 109 | for (size_t i = 0; i < conf->downstreamHostsHashTableSize; i++) { 110 | if (croute->downstreamHostsHashTable[i]) 111 | return 0; 112 | } 113 | return 1; 114 | } 115 | 116 | /** 117 | * Initializes the routing table. 118 | */ 119 | void initRouteTable(void) { 120 | unsigned Ix; 121 | struct IfDesc *Dp; 122 | 123 | // Clear routing table... 124 | routing_table = NULL; 125 | 126 | // Join the all routers group on downstream vifs... 127 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 128 | // If this is a downstream vif, we should join the All routers group... 129 | if( Dp->InAdr.s_addr && ! (Dp->Flags & IFF_LOOPBACK) && Dp->state == IF_STATE_DOWNSTREAM) { 130 | my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", 131 | inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); 132 | 133 | k_join(Dp, allrouters_group); 134 | 135 | my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s", 136 | inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); 137 | k_join(Dp, alligmp3_group); 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * Internal function to send join or leave requests for 144 | * a specified route upstream... 145 | */ 146 | static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { 147 | struct IfDesc* upstrIf; 148 | int i; 149 | 150 | for(i=0; iallowedgroups != NULL) { 162 | bool allow_list = false; 163 | struct SubnetList *match = NULL; 164 | struct SubnetList *sn; 165 | uint32_t group = route->group; 166 | 167 | // Check if this Request is legit to be forwarded to upstream 168 | for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) { 169 | // Check if there is a whitelist 170 | if (sn->allow) 171 | allow_list = true; 172 | if((group & sn->subnet_mask) == sn->subnet_addr) 173 | match = sn; 174 | } 175 | 176 | // Keep in sync with request.c, note the negation 177 | if(!((!allow_list && match == NULL) || 178 | (allow_list && match != NULL && match->allow))) { 179 | my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); 180 | return; 181 | } 182 | } 183 | 184 | // Send join or leave request... 185 | if(join) { 186 | // Only join a group if there are listeners downstream... 187 | if(route->vifBits > 0) { 188 | my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", 189 | inetFmt(route->group, s1), 190 | inetFmt(upstrIf->InAdr.s_addr, s2)); 191 | 192 | k_join(upstrIf, route->group); 193 | 194 | route->upstrState = ROUTESTATE_JOINED; 195 | } else { 196 | my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", 197 | inetFmt(route->group, s1)); 198 | } 199 | } else { 200 | // Only leave if group is not left already... 201 | if(route->upstrState != ROUTESTATE_NOTJOINED) { 202 | my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", 203 | inetFmt(route->group, s1), 204 | inetFmt(upstrIf->InAdr.s_addr, s2)); 205 | 206 | k_leave(upstrIf, route->group); 207 | 208 | route->upstrState = ROUTESTATE_NOTJOINED; 209 | } 210 | } 211 | } 212 | else 213 | { 214 | i = MAX_UPS_VIFS; 215 | } 216 | } 217 | } 218 | 219 | /** 220 | * Clear all routes from routing table, and alerts Leaves upstream. 221 | */ 222 | void clearAllRoutes(void) { 223 | struct RouteTable *croute, *remainroute; 224 | 225 | // Loop through all routes... 226 | for(croute = routing_table; croute; croute = remainroute) { 227 | 228 | remainroute = croute->nextroute; 229 | 230 | // Log the cleanup in debugmode... 231 | my_log(LOG_DEBUG, 0, "Removing route entry for %s", 232 | inetFmt(croute->group, s1)); 233 | 234 | // Uninstall current route 235 | if(!internUpdateKernelRoute(croute, 0)) { 236 | my_log(LOG_WARNING, 0, "The removal from Kernel failed."); 237 | } 238 | 239 | // Send Leave message upstream. 240 | sendJoinLeaveUpstream(croute, 0); 241 | 242 | // Clear memory, and set pointer to next route... 243 | free(croute); 244 | } 245 | routing_table = NULL; 246 | 247 | // Send a notice that the routing table is empty... 248 | my_log(LOG_NOTICE, 0, "All routes removed. Routing table is empty."); 249 | } 250 | 251 | /** 252 | * Private access function to find a route from a given 253 | * Route Descriptor. 254 | */ 255 | static struct RouteTable *findRoute(uint32_t group) { 256 | struct RouteTable* croute; 257 | 258 | for(croute = routing_table; croute; croute = croute->nextroute) { 259 | if(croute->group == group) { 260 | return croute; 261 | } 262 | } 263 | 264 | return NULL; 265 | } 266 | 267 | /** 268 | * Adds a specified route to the routingtable. 269 | * If the route already exists, the existing route 270 | * is updated... 271 | */ 272 | int insertRoute(uint32_t group, int ifx, uint32_t src) { 273 | 274 | struct Config *conf = getCommonConfig(); 275 | struct RouteTable* croute; 276 | 277 | // Sanitycheck the group adress... 278 | if( ! IN_MULTICAST( ntohl(group) )) { 279 | my_log(LOG_WARNING, 0, "The group address %s is not a valid Multicast group. Table insert failed.", 280 | inetFmt(group, s1)); 281 | return 0; 282 | } 283 | 284 | // Santiycheck the VIF index... 285 | if(ifx >= MAXVIFS) { 286 | my_log(LOG_WARNING, 0, "The VIF Ix %d is out of range (0-%d). Table insert failed.", ifx, MAXVIFS-1); 287 | return 0; 288 | } 289 | 290 | // Try to find an existing route for this group... 291 | croute = findRoute(group); 292 | if(croute==NULL) { 293 | struct RouteTable* newroute; 294 | 295 | my_log(LOG_DEBUG, 0, "No existing route for %s. Create new.", 296 | inetFmt(group, s1)); 297 | 298 | 299 | // Create and initialize the new route table entry.. 300 | newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable) + (conf->fastUpstreamLeave ? conf->downstreamHostsHashTableSize : 0)); 301 | // Insert the route desc and clear all pointers... 302 | newroute->group = group; 303 | memset(newroute->originAddrs, 0, MAX_ORIGINS * sizeof(newroute->originAddrs[0])); 304 | newroute->nextroute = NULL; 305 | newroute->prevroute = NULL; 306 | newroute->upstrVif = -1; 307 | 308 | if(conf->fastUpstreamLeave) { 309 | // Init downstream hosts bit hash table 310 | zeroDownstreamHosts(conf, newroute); 311 | 312 | // Add downstream host 313 | setDownstreamHost(conf, newroute, src); 314 | } 315 | 316 | // The group is not joined initially. 317 | newroute->upstrState = ROUTESTATE_NOTJOINED; 318 | 319 | // The route is not active yet, so the age is unimportant. 320 | newroute->ageValue = conf->robustnessValue; 321 | newroute->ageActivity = 0; 322 | 323 | BIT_ZERO(newroute->ageVifBits); // Initially we assume no listeners. 324 | 325 | // Set the listener flag... 326 | BIT_ZERO(newroute->vifBits); // Initially no listeners... 327 | if(ifx >= 0) { 328 | BIT_SET(newroute->vifBits, ifx); 329 | } 330 | 331 | // Check if there is a table already.... 332 | if(routing_table == NULL) { 333 | // No location set, so insert in on the table top. 334 | routing_table = newroute; 335 | my_log(LOG_DEBUG, 0, "No routes in table. Insert at beginning."); 336 | } else { 337 | 338 | my_log(LOG_DEBUG, 0, "Found existing routes. Find insert location."); 339 | 340 | // Check if the route could be inserted at the beginning... 341 | if(routing_table->group > group) { 342 | my_log(LOG_DEBUG, 0, "Inserting at beginning, before route %s",inetFmt(routing_table->group,s1)); 343 | 344 | // Insert at beginning... 345 | newroute->nextroute = routing_table; 346 | newroute->prevroute = NULL; 347 | routing_table = newroute; 348 | 349 | // If the route has a next node, the previous pointer must be updated. 350 | if(newroute->nextroute != NULL) { 351 | newroute->nextroute->prevroute = newroute; 352 | } 353 | 354 | } else { 355 | 356 | // Find the location which is closest to the route. 357 | for( croute = routing_table; croute->nextroute != NULL; croute = croute->nextroute ) { 358 | // Find insert position. 359 | if(croute->nextroute->group > group) { 360 | break; 361 | } 362 | } 363 | 364 | my_log(LOG_DEBUG, 0, "Inserting after route %s",inetFmt(croute->group,s1)); 365 | 366 | // Insert after current... 367 | newroute->nextroute = croute->nextroute; 368 | newroute->prevroute = croute; 369 | if(croute->nextroute != NULL) { 370 | croute->nextroute->prevroute = newroute; 371 | } 372 | croute->nextroute = newroute; 373 | } 374 | } 375 | 376 | // Set the new route as the current... 377 | croute = newroute; 378 | 379 | // Log the cleanup in debugmode... 380 | my_log(LOG_INFO, 0, "Inserted route table entry for %s on VIF #%d", 381 | inetFmt(croute->group, s1),ifx); 382 | 383 | } else if(ifx >= 0) { 384 | 385 | // The route exists already, so just update it. 386 | BIT_SET(croute->vifBits, ifx); 387 | 388 | // Register the VIF activity for the aging routine 389 | BIT_SET(croute->ageVifBits, ifx); 390 | 391 | // Register dwnstrHosts for host tracking if fastleave is enabled 392 | if(conf->fastUpstreamLeave) { 393 | setDownstreamHost(conf, croute, src); 394 | } 395 | 396 | // Log the cleanup in debugmode... 397 | my_log(LOG_INFO, 0, "Updated route entry for %s on VIF #%d", 398 | inetFmt(croute->group, s1), ifx); 399 | 400 | // Update route in kernel... 401 | if(!internUpdateKernelRoute(croute, 1)) { 402 | my_log(LOG_WARNING, 0, "The insertion into Kernel failed."); 403 | return 0; 404 | } 405 | } 406 | 407 | // Send join message upstream, if the route has no joined flag... 408 | if(croute->upstrState != ROUTESTATE_JOINED) { 409 | // Send Join request upstream 410 | sendJoinLeaveUpstream(croute, 1); 411 | } 412 | 413 | logRouteTable("Insert Route"); 414 | 415 | return 1; 416 | } 417 | 418 | /** 419 | * Activates a passive group. If the group is already 420 | * activated, it's reinstalled in the kernel. If 421 | * the route is activated, no originAddr is needed. 422 | */ 423 | int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif) { 424 | struct RouteTable* croute; 425 | int result = 0; 426 | 427 | // Find the requested route. 428 | croute = findRoute(group); 429 | if(croute == NULL) { 430 | my_log(LOG_DEBUG, 0, 431 | "No table entry for %s [From: %s]. Inserting route.", 432 | inetFmt(group, s1),inetFmt(originAddr, s2)); 433 | 434 | // Insert route, but no interfaces have yet requested it downstream. 435 | insertRoute(group, -1, 0); 436 | 437 | // Retrieve the route from table... 438 | croute = findRoute(group); 439 | } 440 | 441 | if(croute != NULL) { 442 | // If the origin address is set, update the route data. 443 | if(originAddr > 0) { 444 | // find this origin, or an unused slot 445 | int i; 446 | for (i = 0; i < MAX_ORIGINS; i++) { 447 | // unused slots are at the bottom, so we can't miss this origin 448 | if (croute->originAddrs[i] == originAddr || croute->originAddrs[i] == 0) { 449 | break; 450 | } 451 | } 452 | 453 | if (i == MAX_ORIGINS) { 454 | i = MAX_ORIGINS - 1; 455 | 456 | my_log(LOG_WARNING, 0, "Too many origins for route %s; replacing %s with %s", 457 | inetFmt(croute->group, s1), 458 | inetFmt(croute->originAddrs[i], s2), 459 | inetFmt(originAddr, s3)); 460 | } 461 | 462 | // set origin 463 | croute->originAddrs[i] = originAddr; 464 | 465 | // move it to the top 466 | while (i > 0) { 467 | uint32_t t = croute->originAddrs[i - 1]; 468 | croute->originAddrs[i - 1] = croute->originAddrs[i]; 469 | croute->originAddrs[i] = t; 470 | i--; 471 | } 472 | } 473 | croute->upstrVif = upstrVif; 474 | 475 | // Only update kernel table if there are listeners ! 476 | if(croute->vifBits > 0) { 477 | result = internUpdateKernelRoute(croute, 1); 478 | } 479 | } 480 | logRouteTable("Activate Route"); 481 | 482 | return result; 483 | } 484 | 485 | 486 | /** 487 | * This function loops through all routes, and updates the age 488 | * of any active routes. 489 | */ 490 | void ageActiveRoutes(void) { 491 | struct RouteTable *croute, *nroute; 492 | 493 | my_log(LOG_DEBUG, 0, "Aging routes in table."); 494 | 495 | // Scan all routes... 496 | for( croute = routing_table; croute != NULL; croute = nroute ) { 497 | 498 | // Keep the next route (since current route may be removed)... 499 | nroute = croute->nextroute; 500 | 501 | // Run the aging round algorithm. 502 | if(croute->upstrState != ROUTESTATE_CHECK_LAST_MEMBER) { 503 | // Only age routes if Last member probe is not active... 504 | internAgeRoute(croute); 505 | } 506 | } 507 | logRouteTable("Age active routes"); 508 | } 509 | 510 | /** 511 | * Counts the number of interfaces a given route is active on 512 | */ 513 | int numberOfInterfaces(struct RouteTable *croute) { 514 | int Ix; 515 | struct IfDesc *Dp; 516 | int result = 0; 517 | // Loop through all interfaces 518 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 519 | // If the interface is used by the route, increase counter 520 | if(BIT_TST(croute->vifBits, Dp->index)) { 521 | result++; 522 | } 523 | } 524 | my_log(LOG_DEBUG, 0, "counted %d interfaces", result); 525 | return result; 526 | } 527 | 528 | /** 529 | * Should be called when a leave message is received, to 530 | * mark a route for the last member probe state. 531 | */ 532 | void setRouteLastMemberMode(uint32_t group, uint32_t src) { 533 | struct Config *conf = getCommonConfig(); 534 | struct RouteTable *croute; 535 | int routeStateCheck = 1; 536 | 537 | croute = findRoute(group); 538 | if(!croute) 539 | return; 540 | 541 | // Check for fast leave mode... 542 | if(conf->fastUpstreamLeave) { 543 | if(croute->upstrState == ROUTESTATE_JOINED) { 544 | // Remove downstream host from route 545 | clearDownstreamHost(conf, croute, src); 546 | } 547 | 548 | // Do route state check if there is no downstream host in hash table 549 | // This host does not have to been the last downstream host if hash collision occurred 550 | routeStateCheck = testNoDownstreamHost(conf, croute); 551 | 552 | if(croute->upstrState == ROUTESTATE_JOINED) { 553 | // Send a leave message right away but only when the route is not active anymore on any downstream host 554 | // It is possible that there are still some interfaces active but no downstream host in hash table due to hash collision 555 | if (routeStateCheck && numberOfInterfaces(croute) <= 1) { 556 | my_log(LOG_DEBUG, 0, "quickleave is enabled and this was the last downstream host, leaving group %s now", inetFmt(croute->group, s1)); 557 | sendJoinLeaveUpstream(croute, 0); 558 | } else { 559 | my_log(LOG_DEBUG, 0, "quickleave is enabled but there are still some downstream hosts left, not leaving group %s", inetFmt(croute->group, s1)); 560 | } 561 | } 562 | } 563 | 564 | // Set the routingstate to last member check if we have no known downstream host left or if fast leave mode is disabled... 565 | if(routeStateCheck) { 566 | croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; 567 | 568 | // Set the count value for expiring... (-1 since first aging) 569 | croute->ageValue = conf->lastMemberQueryCount; 570 | } 571 | } 572 | 573 | 574 | /** 575 | * Ages groups in the last member check state. If the 576 | * route is not found, or not in this state, 0 is returned. 577 | */ 578 | int lastMemberGroupAge(uint32_t group) { 579 | struct RouteTable *croute; 580 | 581 | croute = findRoute(group); 582 | if(croute!=NULL) { 583 | if(croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER) { 584 | return !internAgeRoute(croute); 585 | } else { 586 | return 0; 587 | } 588 | } 589 | return 0; 590 | } 591 | 592 | /** 593 | * Remove a specified route. Returns 1 on success, 594 | * and 0 if route was not found. 595 | */ 596 | static int removeRoute(struct RouteTable* croute) { 597 | struct Config *conf = getCommonConfig(); 598 | int result = 1; 599 | 600 | // If croute is null, no routes was found. 601 | if(croute==NULL) { 602 | return 0; 603 | } 604 | 605 | // Log the cleanup in debugmode... 606 | my_log(LOG_DEBUG, 0, "Removed route entry for %s from table.", 607 | inetFmt(croute->group, s1)); 608 | 609 | //BIT_ZERO(croute->vifBits); 610 | 611 | // Uninstall current route from kernel 612 | if(!internUpdateKernelRoute(croute, 0)) { 613 | my_log(LOG_WARNING, 0, "The removal from Kernel failed."); 614 | result = 0; 615 | } 616 | 617 | // Send Leave request upstream if group is joined 618 | if(croute->upstrState == ROUTESTATE_JOINED || 619 | (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave)) 620 | { 621 | sendJoinLeaveUpstream(croute, 0); 622 | } 623 | 624 | // Update pointers... 625 | if(croute->prevroute == NULL) { 626 | // Topmost node... 627 | if(croute->nextroute != NULL) { 628 | croute->nextroute->prevroute = NULL; 629 | } 630 | routing_table = croute->nextroute; 631 | 632 | } else { 633 | croute->prevroute->nextroute = croute->nextroute; 634 | if(croute->nextroute != NULL) { 635 | croute->nextroute->prevroute = croute->prevroute; 636 | } 637 | } 638 | // Free the memory, and set the route to NULL... 639 | free(croute); 640 | croute = NULL; 641 | 642 | logRouteTable("Remove route"); 643 | 644 | return result; 645 | } 646 | 647 | 648 | /** 649 | * Ages a specific route 650 | */ 651 | int internAgeRoute(struct RouteTable* croute) { 652 | struct Config *conf = getCommonConfig(); 653 | int result = 0; 654 | 655 | // Drop age by 1. 656 | croute->ageValue--; 657 | 658 | // Check if there has been any activity... 659 | if( croute->ageVifBits > 0 && croute->ageActivity == 0 ) { 660 | // There was some activity, check if all registered vifs responded. 661 | if(croute->vifBits == croute->ageVifBits) { 662 | // Everything is in perfect order, so we just update the route age. 663 | croute->ageValue = conf->robustnessValue; 664 | //croute->ageActivity = 0; 665 | } else { 666 | // One or more VIF has not gotten any response. 667 | croute->ageActivity++; 668 | 669 | // Update the actual bits for the route... 670 | croute->vifBits = croute->ageVifBits; 671 | } 672 | } 673 | // Check if there have been activity in aging process... 674 | else if( croute->ageActivity > 0 ) { 675 | 676 | // If the bits are different in this round, we must 677 | if(croute->vifBits != croute->ageVifBits) { 678 | // Or the bits together to insure we don't lose any listeners. 679 | croute->vifBits |= croute->ageVifBits; 680 | 681 | // Register changes in this round as well.. 682 | croute->ageActivity++; 683 | } 684 | } 685 | 686 | // If the aging counter has reached zero, its time for updating... 687 | if(croute->ageValue == 0) { 688 | // Check for activity in the aging process, 689 | if(croute->ageActivity>0) { 690 | 691 | my_log(LOG_DEBUG, 0, "Updating route after aging : %s", 692 | inetFmt(croute->group,s1)); 693 | 694 | // Just update the routing settings in kernel... 695 | internUpdateKernelRoute(croute, 1); 696 | 697 | // We append the activity counter to the age, and continue... 698 | croute->ageValue = croute->ageActivity; 699 | croute->ageActivity = 0; 700 | } else { 701 | 702 | my_log(LOG_DEBUG, 0, "Removing group %s. Died of old age.", 703 | inetFmt(croute->group,s1)); 704 | 705 | // No activity was registered within the timelimit, so remove the route. 706 | removeRoute(croute); 707 | } 708 | // Tell that the route was updated... 709 | result = 1; 710 | } 711 | 712 | // The aging vif bits must be reset for each round... 713 | BIT_ZERO(croute->ageVifBits); 714 | 715 | return result; 716 | } 717 | 718 | /** 719 | * Updates the Kernel routing table. If activate is 1, the route 720 | * is (re-)activated. If activate is false, the route is removed. 721 | */ 722 | int internUpdateKernelRoute(struct RouteTable *route, int activate) { 723 | struct MRouteDesc mrDesc; 724 | struct IfDesc *Dp; 725 | unsigned Ix; 726 | int i; 727 | 728 | for (i = 0; i < MAX_ORIGINS; i++) { 729 | if (route->originAddrs[i] == 0 || route->upstrVif == -1) { 730 | continue; 731 | } 732 | 733 | // Build route descriptor from table entry... 734 | // Set the source address and group address... 735 | mrDesc.McAdr.s_addr = route->group; 736 | mrDesc.OriginAdr.s_addr = route->originAddrs[i]; 737 | 738 | // clear output interfaces 739 | memset( mrDesc.TtlVc, 0, sizeof( mrDesc.TtlVc ) ); 740 | 741 | my_log(LOG_DEBUG, 0, "Vif bits : 0x%08x", route->vifBits); 742 | 743 | mrDesc.InVif = route->upstrVif; 744 | 745 | // Set the TTL's for the route descriptor... 746 | for ( Ix = 0; (Dp = getIfByIx(Ix)); Ix++ ) { 747 | if(Dp->state == IF_STATE_UPSTREAM) { 748 | continue; 749 | } 750 | else if(BIT_TST(route->vifBits, Dp->index)) { 751 | my_log(LOG_DEBUG, 0, "Setting TTL for Vif %d to %d", Dp->index, Dp->threshold); 752 | mrDesc.TtlVc[ Dp->index ] = Dp->threshold; 753 | } 754 | } 755 | 756 | // Do the actual Kernel route update... 757 | if(activate) { 758 | // Add route in kernel... 759 | addMRoute( &mrDesc ); 760 | } else { 761 | // Delete the route from Kernel... 762 | delMRoute( &mrDesc ); 763 | } 764 | } 765 | 766 | return 1; 767 | } 768 | 769 | /** 770 | * Debug function that writes the routing table entries 771 | * to the log. 772 | */ 773 | void logRouteTable(const char *header) { 774 | struct Config *conf = getCommonConfig(); 775 | struct RouteTable *croute = routing_table; 776 | unsigned rcount = 0; 777 | 778 | my_log(LOG_DEBUG, 0, ""); 779 | my_log(LOG_DEBUG, 0, "Current routing table (%s):", header); 780 | my_log(LOG_DEBUG, 0, "-----------------------------------------------------"); 781 | if(croute==NULL) { 782 | my_log(LOG_DEBUG, 0, "No routes in table..."); 783 | } else { 784 | do { 785 | char st = 'I'; 786 | char src[MAX_ORIGINS * 30 + 1]; 787 | src[0] = '\0'; 788 | int i; 789 | 790 | for (i = 0; i < MAX_ORIGINS; i++) { 791 | if (croute->originAddrs[i] == 0) { 792 | continue; 793 | } 794 | st = 'A'; 795 | sprintf(src + strlen(src), "Src%d: %s, ", i, inetFmt(croute->originAddrs[i], s1)); 796 | } 797 | 798 | my_log(LOG_DEBUG, 0, "#%d: %sDst: %s, Age:%d, St: %c, OutVifs: 0x%08x, dHosts: %s", 799 | rcount, src, inetFmt(croute->group, s2), 800 | croute->ageValue, st, 801 | croute->vifBits, 802 | !conf->fastUpstreamLeave ? "not tracked" : testNoDownstreamHost(conf, croute) ? "no" : "yes"); 803 | 804 | croute = croute->nextroute; 805 | 806 | rcount++; 807 | } while ( croute != NULL ); 808 | } 809 | 810 | my_log(LOG_DEBUG, 0, "-----------------------------------------------------"); 811 | } 812 | 813 | /** 814 | * Returns true when the given group belongs to the given interface 815 | */ 816 | int interfaceInRoute(int32_t group, int Ix) { 817 | struct RouteTable* croute; 818 | croute = findRoute(group); 819 | if (croute != NULL) { 820 | my_log(LOG_DEBUG, 0, "Interface id %d is in group %d", Ix, group); 821 | return BIT_TST(croute->vifBits, Ix); 822 | } else { 823 | return 0; 824 | } 825 | } 826 | -------------------------------------------------------------------------------- /src/syslog.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** igmpproxy - IGMP proxy based multicast router 3 | ** Copyright (C) 2005 Johnny Egeland 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | ** 19 | **---------------------------------------------------------------------------- 20 | ** 21 | ** This software is derived work from the following software. The original 22 | ** source code has been modified from it's original state by the author 23 | ** of igmpproxy. 24 | ** 25 | ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill 26 | ** - Licensed under the GNU General Public License, either version 2 or 27 | ** any later version. 28 | ** 29 | ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of 30 | ** Leland Stanford Junior University. 31 | ** - Licensed under the 3-clause BSD license, see Stanford.txt file. 32 | ** 33 | */ 34 | 35 | #include "igmpproxy.h" 36 | 37 | int LogLevel = LOG_WARNING; 38 | bool Log2Stderr = false; 39 | 40 | void my_log( int Severity, int Errno, const char *FmtSt, ... ) 41 | { 42 | char LogMsg[ 128 ]; 43 | 44 | va_list ArgPt; 45 | unsigned Ln; 46 | va_start( ArgPt, FmtSt ); 47 | Ln = vsnprintf( LogMsg, sizeof( LogMsg ), FmtSt, ArgPt ); 48 | if( Errno > 0 ) 49 | snprintf( LogMsg + Ln, sizeof( LogMsg ) - Ln, 50 | "; Errno(%d): %s", Errno, strerror(Errno) ); 51 | va_end( ArgPt ); 52 | 53 | if (Severity <= LogLevel) { 54 | if (Log2Stderr) 55 | fprintf(stderr, "%s\n", LogMsg); 56 | else { 57 | syslog(Severity, "%s", LogMsg); 58 | } 59 | } 60 | 61 | if( Severity <= LOG_ERR ) 62 | exit( -1 ); 63 | } 64 | --------------------------------------------------------------------------------