pax_global_header00006660000000000000000000000064126703511260014515gustar00rootroot0000000000000052 comment=6b91405516e9f919f2df23f7794d00e916fc1871 pimd-2.3.2/000077500000000000000000000000001267035112600124525ustar00rootroot00000000000000pimd-2.3.2/.gdbinit000066400000000000000000000000551267035112600140730ustar00rootroot00000000000000set args -d pim_mrt,pim_bsr set args -d all pimd-2.3.2/.gitignore000066400000000000000000000001231267035112600144360ustar00rootroot00000000000000vers.c pimd pimd.map ChangeLog.* *.o .*.d *~ \#* .\#* build-stamp configure-stamp pimd-2.3.2/.gitmodules000066400000000000000000000001231267035112600146230ustar00rootroot00000000000000[submodule "libite"] path = libite url = https://github.com/troglobit/libite.git pimd-2.3.2/.travis.yml000066400000000000000000000013031267035112600145600ustar00rootroot00000000000000# Travis CI integration sudo: false language: c compiler: - gcc env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "OAx9MdLgm+ZpCo/h65praDhKl35RgHEQRFR1qa5fLFrVxqFIusXFl4/A+xSpY+EYcmN0Vcsn5Z79dy3DqK6Njbt14s56NH1mvuDw7SxkFDijwZkJTmgjgCiDM+Hn3Vwjk9WYX0PNifX8Mcm0Zp9sci/koW523ltJdWC7ULvFzK8=" addons: coverity_scan: project: name: "troglobit/pimd" description: "pimd | The Original PIM-SM Daemon" notification_email: troglobit@gmail.com build_command_prepend: "./configure" build_command: "make" branch_pattern: dev script: ./configure && make pimd-2.3.2/AUTHORS000066400000000000000000000003421267035112600135210ustar00rootroot00000000000000Current Maintainer(s): * Joachim Nilsson Original Author(s): * Ahmed Helmy * George Edmond "Rusty" Eddy * Pavlin Ivanov Radoslavov pimd-2.3.2/CODE-OF-CONDUCT.md000066400000000000000000000036221267035112600151100ustar00rootroot00000000000000# Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant][1], [version 1.2.0][2]. [1]: http://contributor-covenant.org [2]: http://contributor-covenant.org/version/1/2/0/ pimd-2.3.2/CONTRIBUTING.md000066400000000000000000000104001267035112600146760ustar00rootroot00000000000000Contributing to pimd ==================== pimd is an old project. As such it has a lot of history that sometimes creep up through the cracks. Bugs! Bugs can be missing or unclear documentation, faulty legacy behavior, missing key features or support for a new cool operating system. Or simple things, like speling errors or too frequent, or missing logs. pimd exists on [GitHub][github] for this exact purpose -- to fix bugs, collaboratively. We welcome any and all help in the form of bug reports, fixes, patches for new features -- *preferably as GitHub pull requests*, submitting a pull request practically guarantees inclusion ... other methods are of course also possible: emailing the maintainer a patch or even a raw file, or simply emailing a feature request or an alert for a problem. For email questions/requests/alerts there is always the risk of memory exhaustion on the part of the maintainer. Coding Style ------------ > **Tip:** Adapt your code to what the surrounding code looks like! pimd is written in C. C is an old language and sometimes that age is clearly visible. The current maintainer has tried to (re)enforce a consistent and more modern C style, without having to completly re-indent the whole code base. First of all, lines are allowed to be longer than 72 characters these days. In fact, there exist no enforced maximum, but keeping it around 100 chars is OK. pimd use leading four spaces in functions and structs. The next level use eight spaces, but the original authors used tab for that level. A `switch()` has its `case` statements indented four spaces ... but then it is more or less [KNF][]. In Emacs this coding style can be achieved by using the following footer applied to a C file: /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ The current maintainer has considered shifting the original coding style to FULL [KNF][] several times. GIT Submodules -------------- pimd is maintained using the GIT version control system. It also use a Git submodule, [libite][], to provide several utilities functions, like `pidfile()` and the OpenBSD `strlcpy()` family of functions. Make sure to `git submodule update` after a `git pull`, in addition to `git submodule update --init` when you clone the repository initially. Commit Messages --------------- Commit messages exist to track *why* a change was made. Try to be as clear and concise as possible in your commit messages. Example from the [Pro Git][gitbook] online book: Brief, but clear and concise summary of changes More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the ummary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here Another good *counter* example [is this][rambling] ... Target Systems -------------- pimd mainly targets modern UNIX systems and we semi-regularly test it on both Debian and Ubuntu for Linux, FreeBSD, NetBSD, and OpenBSD. Please consider these targets when submitting new features. If you cannot test on other operating systems yourself, be prepared that your feature/fix will likely be delayed by the maintaier, who will attempt to test and in some cases port the feature for you. Code of Conduct --------------- It is expected of everyone engaging in the project to, in the words of Bill & Ted; [be excellent to each other][conduct]. [github]: https://github.com/troglobit/pimd/ [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form [libite]: https://github.com/troglobit/libite [gitbook]: https://git-scm.com/book/ch5-2.html [rambling]: http://stopwritingramblingcommitmessages.com/ [conduct]: https://github.com/troglobit/pimd/blob/master/CODE-OF-CONDUCT.md pimd-2.3.2/CREDITS000066400000000000000000000064041267035112600134760ustar00rootroot00000000000000Credits ======= Apologies if your names is not included, please contact us to be included. * PIM-SSM and IGMPv3 support added by Markus Veranen * The pimd maintainer until January 14, 2005, when the project was officially retired: Pavlin Ivanov Radoslavov * The PIM kernel modifications and pimd itself were originally written by Ahmed Helmy (ahelmy@catarina.usc.edu) as a summer intern in SGI. * The PIM-SM implementation for gated, George Edmond "Rusty" Eddy * The "up to the March '97 I-D spec" + RSVP support pimd version was done by Pavlin Radoslavov during his summer'97 intern in Sun Microsystems under Michael Speer's supervision. * BSDI 3.0/3.1 support + various improvements and bug reports by Hitoshi Asaeda (asaeda@yamato.ibm.co.jp). * Bug reports and SGI tests by Nidhi Bhaskar (nidhi@cho-oyu.engr.sgi.com). * Bug reports and SunOS tests by Isabelle Girard (girardi@rc.bel.alcatel.be) and Dirk Ooms (oomsd@rc.bel.alcatel.be) * NetBSD-1.3 compilation support (both for pimd and the kernel mods) and bug reports by Heiko W.Rupp * Bug reports by Chirayu Shah (shahzad@torrentnet.com) * A number of changes copied back from pimdd (PIM-DM) stand-alone implementation by Kurt Windisch (kurtw@antc.uoregon.edu) * Linux patches by "Jonathan Day" and Fred Griffoul * Bug reports and Linux tests by Kaifu Wu * Linux kernel internals help by Alexey Kuznetsov * Bug reports by Fred Griffoul * Bug reports and fixes for Linux by "Venning, Roger" * Various patches and bug fixes by JINMEI Tatuya * Solaris 8 fixes by "Eric S. Johnson" * Bug reports by Azzurra Pantella and Nicola Dicosmo from University of Pisa. * Bug reports and fixes by Sri V * Bug reports, fixes and new features (ALTNET, SCOPED traffic) by Marian Stagarescu and Ashok Rao * Bug reports by Philip Ho * Bug reports by * GRE configuration examples, bug fixes, and code contribution by Hiroyuki Komatsu * Bug report and fix by Xiaofeng Liu * Bug reports and fixes by SAKAI Hiroaki * Bug reports and fixes by "Jiahao Wang" and "Bo Cheng" ) * Bug report by Andrea Gambirasio * Bug reports by "J.W. (Bill) Atwood" * Bug report by "Eva Pless" * Bug reports and fixes by SUZUKI Shinsuke * Bug report by Serdar Uezuemcue * Bug reports and fixes by * All alpha testers who were brave enough to try it and then sent me feedbacks (apology for not keeping the list). * Thanks to the FreeBSD team and particularly to the freebsd-hackers mailing list participants for the help with the real-time debugging of the FreeBSD kernel. pimd-2.3.2/ChangeLog.org000066400000000000000000001262601267035112600150210ustar00rootroot00000000000000#+TITLE: pimd | Change Log #+AUTHOR: Ahmed Helmy, George Edmond Eddy, Pavlin Ivanov Radoslavov, and Markus Veranen #+OPTIONS: toc:nil #+OPTIONS: H:3 num:0 #+LaTeX_HEADER: \usepackage{parskip} \usepackage{a4wide} #+LaTeX_CLASS_OPTIONS: [twoside, colorlinks=true, linkcolor=blue, urlcolor=blue] * Version 2.3.2: March 10, 2016 Bug fix release. All users should upgrade, in particular FreeBSD users! ** Changes - Minor code cleanup and readability changes to simplify the code. - Update to libite v1.4.2 with improved =min()=/=max()= macros - Use =-Wextra= not =-Werror= in default =CFLAGS=, this to ensure that pimd still builds OK on newer and more pedantic compilers - Update man page and example `pimd.conf` with details on `rp-candidate` `bsr-candidate`, two very important settings for correct operation. ** Bug Fixes - Issue #57: Multicast routing table not updated on FreeBSD. Introduced with issue #23, in pimd v2.2.0. Too intrusive changes altered handling (forwarding) of PIM register messages. This only affects BSD systems, in particular FreeBSD 10.2 (current), or any FreeBSD < 11.0 - Issue #63: Mika Joutsenvirta found and fixed serious issues with the PIM Assert timeout handling. - Issue #65: Missing slash in config file path when using env. variable - Issue #66: Make it possible to run =pimd= without a configuration file. If =pimd= cannot find its configuration file it will use built-in fallback settings for =bsr-candidate= and =rp-candidate=. This to ensure you do not end up with a non-working setup. To disable =bsr-candidate= and =rp-candidate=, simply leave them out of your config file, and make sure =pimd= can find the file. - Issue #69: Rate limit only what is actually logged. The =logit()= function counted filtered messages, causing long periods of silence for no reason. Fix by Apollon Oikonomopoulos #+LATEX: \newpage * Version 2.3.1: November 15, 2015 Bug fix release. ** Changes - Let build system handle missing libite GIT submodule - Issue #61: Debian packaging moved to https://github.com/bobek/pkg-pimd ** Bug Fixes - Issue #53: Build problem with Clang on FreeBSD - Issue #55: Default config uses =/etcpimd.conf= instead of =/etc/pimd.conf=. Slashes added and now =pimd -h= lists the default path instead of a hard coded string. - Issue #60: Fix minor spelling errors. #+LATEX: \newpage * Version 2.3.0: July 31, 2015 -- /PIM-SSM & IGMPv3 release!/ The significant new features in this release would not have been possible if not for the hard work of Markus Veranen Tested on Ubuntu 14.04 (GLIBC/Linux 3.13), Debian 8.1 (GLIBC/Linux 3.16), FreeBSD, NetBSD, and OpenBSD. ** Changes and New Features - Support for PIM-SSM and IGMPv3, by Markus Veranen - IGMPv3 is now default, use =phyint ifname igmpv2= for old behaviour - Default IGMP query interval has changed from 125 sec to 12 sec In =pimd.conf: igmp-query-interval = - Default IGMP querier timeout has changed from 255 sec to 42 sec In =pimd.conf: igmp-querier-timeout = - The built-in IGMP /robustness value/ changed from 2 to 3 - Support for changing the PIM Hello interval, by Markus Veranen In =pimd.conf: hello-interval = - Support for multiple multicast routing tables, and running multiple pimd instances, by Markus Veranen. (Only supported on Linux atm.) - Support for advertising, and acting upon changes to, Generation ID in PIM Hello messages, by Markus Veranen - Support for advertising /DR Priority/ option in PIM Hello messages. If all routers on a LAN send this option this value is used in the DR election rather than the IP address. The priority is configured per =phyint=. This closes the long-standing issue #5. - Distribution archive format changed from XZ to Gzip, for the benefit of OpenBSD that only ships Gzip in the base system. ** New pimd.conf syntax! The =pimd.conf= syntax has been changed in this release. Mainly, the configuration file now use dashes =-= instead of underscore =_= as word separators. However several settings have also been renamed to be more familiar to commands used by major router vendors: - =bsr-candidate= :: replaces =cand_bootstrap_router= - =rp-candidate= :: replaces =cand_rp= - =group-prefix= :: replaces =group_prefix= - =rp-address= :: replaces =rp_address= - =spt-threshold= :: replaces the two deprecated =switch_register_threshold= and =switch_data_threshold= settings - =hello-interval= :: replaces =hello_period= - =default-route-distance= :: replaces =default_source_preference= - =default-route-metric= :: replaces =default-source-metric= Also, for =phyint= the =preference= sub-option has been replaced with the less confusing =distance= and =ttl-threshold= replaces =threshold=. See the README or the man page for more information on the metric preference and admin distance confusion. /*Note:* The =pimd.conf= parser remains backwards compatible with the old syntax!/ ** Compile Time Features The following are new features that must be enabled at compile time, using the =configure= script, to take effect. For details, see =./configure --help= - =--prefix=PATH= :: Standard prefix to be used at installation, default =/usr/local= - =--sysconfdir=PATH= :: Prefix path to be used for =pimd.conf=, default =/etc=, unless =--prefix= is given. - =--embedded-libc= :: Enable uClib or musl libc build, on Linux. - =--disable-exit-on-error= :: Allow pimd to continue running despite encountering errors. - =--disable-pim-genid= :: Disable advertisement of PIM Hello GenID, use for compatibility problems with older versions of pimd. - =--with-max-vifs=MAXVIFS= :: Raise max number of VIFs to MAXVIFS. *Note:* this requires raising MAXVIFS in the kernel as well! Most kernels cannot handle >255, if this is a problem, try using multiple multicast routing tables instead. - =--disable-masklen-check= :: Allow tunctl VIFs with masklen 32. ** Bug Fixes - Fix issue #40: FTBS with =./configure --enable-scoped-acls= - Properly support cross compiling. It is now possible to actually define the =$CROSS= environment variable when calling =make= to allow cross compiling pimd. Should work with both GCC and Clang. Tested on Ubuntu, Debian and FreeBSD. #+LATEX: \newpage * Version 2.2.1: April 20, 2015 ** Bug Fixes - Fix another problem with issue #22 (reopened), as laid out in issue #37. This time the crash is induced when there is a link down event. Lot of help debugging the propblem by @mfspeer, who also suggested the fix -- to call =pim_proto.c:delete_pim_nbr()= in =vif.c:stop_vif()= instead of just calling free. - Fix issue with not checking return value of =open()= in daemonizing code in =main()=, found by Coverity Scan. - Fix issue with scoped =phyint= in =config.c=, found by Coverity Scan. The =masklen= may not be zero, config file problem, alert the user. #+LATEX: \newpage * Version 2.2.0: December 28, 2014 ** Changes & New Features - Support for IP fragmentation of PIM register messages, by Michael Fine, Cumulus Networks - Support =/LEN= syntax in =phyint= to complement =masklen LEN=, issue #12 - Add support for /31 networks, point-to-point, thanks to Apollon Oikonomopoulos - Remove old broken SNMP support - OpenBSD inspired cleanup (deregister) - General code cleanup, shorten local variable names, func decl. etc. - Support for router alert IP option in IGMP queries - Support for reading IGMPv3 membership reports - Update IGMP code to support FreeBSD >= 8.x - Retry read of routing tables on FreeBSD - Fix join/leve of ALL PIM Routers for FreeBSD and other UNIX kernels - Tested on FreeBSD, NetBSD and OpenBSD - Add very simple homegrown configure script - Update and document support for =rp_address=, =cand_rp=, and =cand_bootstrap_router= - Add new =spt_threshold= to replace existing =switch_register_threshold= and =switch_data_threshold settings=. Cisco-like and easier to understand ** Bug Fixes - Fix to avoid infinite loop during unicast send failure, by Alex Tessmer - Fix bug in bootstrap when configured as candidate RP, issue #15 - Fix segfault in =accept_igmp()=, issue #29 - Fix default source preference, should be 101 (not 1024!) - Fix =ip_len= handling on older BSD's, thanks to Olivier Cochard-Labbé, issue #23 - Fix default prefix len in static RP example in =pimd.conf=, should be /4 - Fix issue #31: Make IGMP query interval and querier timeout configurable - Fix issue #33: pimd does not work in background under FreeBSD - Fix issue #35: support for timing out other queriers from mrouted - Hopefully fix issue #22: Crash in (S,G) state when neighbor is lost - Misc. bug fixes thanks to Coverity Scan, static code analysis tool https://scan.coverity.com/projects/3319 #+LATEX: \newpage * Version 2.1.8: October 22, 2011 ** Changes & New Features - Update docs of static Rendez-Vous Point, =rp_address=, configuration in man page and example =pimd.conf=. Thanks to Andriy Senkovych and YAMAMOTO Shigeru - Replaced =malloc()= with =calloc()= to mitigate risk of accessing junk data and ease debugging. Thanks to YAMAMOTO Shigeru - Extend .conf file =rp_address= option with =priority= field. Code changes and documentation updates by YAMAMOTO Shigeru ** Bug Fixes - A serious bug in =pim_proto.c:receive_pim_register()= was found and fixed by Jean-Pascal Billaud. In essence, the RP check was broken since the code only looked at =my_cand_rp_address=, which is not set when using the =rp_address= config. Everything works fine with auto-RP mode though. This issue completely breaks the register path since the JOIN(S,G) is never sent back ... - Fix FTBFS issues reported from Debian. Later GCC versions trigger unused variable warnings. Patches and cleanup Antonin Kral * Version 2.1.7: January 9, 2011 ** Changes & New Features - The previous move of runtime dump files to =/var/lib/misc= have been changed to =/var/run/pimd= instead. This to accomodate *BSD systems that do not have the =/var/lib= tree, and also recommended in the Filesystem Hierarchy Standard, http://www.pathname.com/fhs/pub/fhs-2.3.html#VARRUNRUNTIMEVARIABLEDATA #+LATEX: \newpage * Version 2.1.6: January 8, 2011 ** Changes & New Features - Debian package now conflicts with =smcroute=, in addition to =mrouted=. It is only possible to run one multicast routing daemon at a time, kernel limitation. - The location of the dump file(s) have been moved from =/var/tmp= to =/var/lib/misc= due to the insecure nature of =/var/tmp=. See more below. ** Bug Fixes - =kern.c:k_del_vif()=: Fix build error on GNU/kFreeBSD - CVE-2011-0007: Insecure file creation in =/var/tmp=. "On USR1, pimd will write to =/var/tmp/pimd.dump= a dump of the multicast route table. Since =/var/tmp= is writable by any user, a user can create a symlink to any file he wants to destroy with the content of the multicast routing table." * Version 2.1.5: November 21, 2010 ** Changes & New Features - Improved error messages in kern.c - Renamed CHANGES to ChangeLog ** Bug Fixes - Import mrouted fix: on GNU/Linux systems (only!) the call to =kern.c:k_del_vif()= fails with: =setsockopt MRT_DEL_VIF on vif 3: Invalid argument=. This is due to differences in the Linux and *BSD =MRT_DEL_VIF= API. The Linux kernel expects to receive a =struct vifctl= associated with the VIF to be deleted, *BSD systems on the other hand expect to receive the index of that VIF. Bug reported and fixed on mrouted by Dan Kruchinin #+LATEX: \newpage * Version 2.1.4: September 25, 2010 ** Changes & New Features - Updates for support on Debian GNU/kFreeBSD, FreeBSD kernel with GNU userland. ** Bug Fixes - Lior Dotan reports that pimd 2.1.2 and 2.1.3 are severely broken w.r.t. uninformed systematic replace of =bcopy()= with =memcpy()= API. * Version 2.1.3: September 8, 2010 ** Changes & New Features - =debug.c:syslog()=: Removed GNU:ism %m, use =strerror(errno)= instead. - Cleanup and ansification of a couple of files: rp.c, mrt.c, vif.c, route.c - Initialize stack variables to silence overzealous GCC on PowerPC and S/390. Debian bug 595584, this closes pimd issue #3 on GitHub. ** Bug Fixes - Merge bug fix for static-rp configurations from Kame's pim6sd route.c r1.28 - Close TODO item by merging in relevant changes from Kame's pim6sd =vif.c r1.3= - Tried fixing =debug.c:logit()= build failure on Sparc due to mixup in headers for =tv_usec= type. #+LATEX: \newpage * Version 2.1.2: September 4, 2010 ** Changes & New Features - License change on mrouted code from OpenBSD team => pimd fully free under the simlified 3-clause BSD license! This was also covered in v2.1.0-alpha29.17, but now all files have been updated, including LICENSE.mrouted. - Code cleanup and ansification. - Simplified Makefile so that it works seamlessly on GNU Make and BSD PMake. - Replaced all calls to =bzero()= and =bcopy()= with =memset()= and =memcpy()=. - Use =getopt_long()= for argument parsing. - Add, and improve, -h,--help output. - Add -f,--foreground option. - Add -v,--version option. - Add -l,--reload-config which sends SIGHUP to a running daemon. - Add -r,--show-routes which sends SIGUSR1 to a running daemon. - Add -q,--quit-daemon which sends SIGTERM to a running daemon. - Make it possible to call pimd as a regular user, for --help and --version. - Man page cleaned up, a lot, and updated with new options. ** Bug Fixes - Replaced dangerous old string functions with safer =snprintf()= and =strlcpy()= - Added checks for =malloc()= return values, all over the code base. - Fixed issues reported by Sparse (CC=cgcc). - Make sure to retry syscalls =recvfrom()= and =sendto()= on signal (SIGINT). - Fix build issues on OpenBSD 4.7 and FreeBSD 8.1 thanks to Guillaume Sellier. - Kernel include issues on Ubuntu 8.04, Linux <= 2.6.25, by Nikola Knežević - Fix build issues on NetBSD #+LATEX: \newpage * Version 2.1.1: January 17, 2010 Merged all patches from http://lintrack.org. ** Changes & New Features - Bumping version again to celebrate the changes and make it easier for distributions to handle the upgrade. - =002-better-rp_address.diff=: Support multicast group address in static Rendez-Vous Point .conf option. - =004-disableall.diff=: Add -N option to pimd. - =005-vifenable.diff=: Add enable keyword to phyint .conf option. ** Bug Fixes - =001-debian-6.diff=: Already merged, no-op - only documenting in case anyone wonders about it. - =003-ltfixes.diff=: Various bug fixes and error handling improvements. - =006-dot19.diff=: The lost alpha29.18 and alpha29.19 fixes by Pavlin Radoslavov. * Version 2.1.0, January 16, 2010 ** Changes & New Features - Integrated the latest Debian patches from =pimd_2.1.0-alpha29.17-9.diff.gz= - Fixed the new file include/linux/netinet/in-my.h (Debian) so that the #else fallback uses the system netinet/in.h, which seems to work now. - Bumped version number, this code has been available for a while now. #+LATEX: \newpage * Version 2.1.0-alpha29.19: January 14, 2005 ** Bug Fixes - Don't ignore PIM Null Register messages if the IP version of the inner header is not valid. - Add a missing bracket inside rsrr.c (a bug report and a fix by ) * Version 2.1.0-alpha29.18: May 21, 2003 ** Changes & New Features - Compilation fix for Solaris 8. Though, no guarantee pimd still works on that platform. - Define =BYTE_ORDER= if missing. - Update include/netinet/pim.h file with its lastest version - Update the copyright message of =include/netinet/pim_var.h= * Version 2.1.0-alpha29.17: March 20, 2003 ** Changes & New Features - The mrouted license, LICENSE.mrouted, updated with BSD-like license!! Thanks to the OpenBSD folks for the 2 years of hard work to make this happen: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/mrouted/LICENSE - Moved the pimd contact email address upfront in README. Let me repeat that here: If you have any questions, suggestions, bug reports, etc., do NOT send them to the PIM IETF Working Group mailing list! Instead, use the contact email address specified in README. * Version 2.1.0-alpha29.16: February 18, 2003 ** Bug Fixes - Compilation bugfix for Linux. Bug report by Serdar Uezuemcue * Version 2.1.0-alpha29.15: February 12, 2003 ** Bug Fixes - Routing socket descriptor leak. Bug report and fix by SUZUKI Shinsuke ; incorporated back from pim6sd. - PIM join does not go upstream. Bug report and fix by SUZUKI Shinsuke ; incorporated back from pim6sd. #+BEGIN_EXAMPLE [problem] PIM join does not go upstream in the following topology, because oif-list is NULL after subtracting iif from oif-list. receiver---rtr1---| rtr2---|---rtr3----sender rtr1's nexthop to sender = rtr2 rtr2's nexthop to sender = rtr3 [reason] Owing to a difference between RFC2362 and the new pim-sm draft. [solution] Prunes iif from oiflist when installing it into kernel, instead of PIM route calculation time. #+END_EXAMPLE * Version 2.1.0-alpha29.14: February 10, 2003 ** Bug Fixes - Bugfix in calculating the netmask for POINTOPOINT interface in config.c. Bug report by J.W. (Bill) Atwood - =rp.c:rp_grp_match()=: SERIOUS bugfix in calculating the RP per group when there are a number of group prefixes in the Cand-RP set. Bug report by Eva Pless * Version 2.1.0-alpha29.13: November 7, 2002 ** Bug Fixes - Bugfix in rp.c =bootstrap_initial_delay()= in calculating BSR election delay. Fix by SAKAI Hiroaki * Version 2.1.0-alpha29.12: September 26, 2002 ** Bug Fixes - Increase size of send buffers in the kernel. Bug report by Andrea Gambirasio * Version 2.1.0-alpha29.11: July 8, 2002 ** Bug Fixes Bug reports and fixes by SAKAI Hiroaki - =init_routesock()=: Bugfix: initializing a forgotten variable. The particular code related to that variable is commented-out by default, but a bug is a bug. - =main.c:restart()=: Bugfix: close the =udp_socket= only when it is is different from =igmp_socket=. - =main.c:main()=: if SIGHUP signal is received, reconstruct readers and nfds - Three serious bug fixes thanks to Jiahao Wang and Bo Cheng : - =pim_proto.c:receive_pim_join_prune()=: two bugfixes related to the processing of (*,*,RP) - =pim_proto.c:add_jp_entry()=: Bugfix regarding adding prune entries - Remove the FTP URL from the various README files, and replace it with an HTTP URL, because the FTP server on catarina.usc.edu is not operational anymore. * Version 2.1.0-alpha29.10: April 26, 2002 ** Bug Fixes - Widen the space for "Subnet" addresses printed under "Virtual Interface Table" - Added (commented-out code) to enable different interfaces to belong to overlapping subnets. See around line 200 in config.c - Bugfix in handling of Join/Prune messages when there is one join and one prune for the same group. Thanks to Xiaofeng Liu . * Version 2.1.0-alpha29.9: November 13, 2001 ** Changes & New Features First three entries contributed by Hiroyuki Komatsu - Print line number if there is conf file error. - If there is an error in the conf file, pimd won't start. - GRE configuration examples added to README.config. - New file README.debug (still very short though). ** Bug Fixes - Increase the config line buffer size to 1024. Bug fix by Hiroyuki Komatsu * Version 2.1.0-alpha29.8: September 16, 2001 ** Changes & New Features - Better log messages for point-to-point links in config.c. Thanks to Hitoshi Asaeda * Version 2.1.0-alpha29.7: September 10, 2001 ** Changes & New Features - Added "phyint altnet" (see pimd.conf for usage) for allowing some senders look like directly connected to a local subnet. Implemented by Marian Stagarescu - Added "phyint scoped" (see pimd.conf for usage) for administartively disabling the forwarding of multicast groups. Implemented by Marian Stagarescu - The License has changed from the original USC to the more familiar BSD-like (the KAME+OpenBSD guys brought to my attention that the original working in the USC license "...and without fee..." is ambiguous and makes it sound that noone can distribute pimd as part of some other software distribution and charge for that distribution. - RSRR disabled by default in Makefile ** Bug Fixes - Memory leaks bugs fixed in rp.c, thanks to Sri V - Compilation problems for RedHat-7.1 fixed. Bug report by Philip Ho - PID computation fixed (it should be recomputed after a child =fork()=). Thanks to Marian Stagarescu - =find_route()=-related bug fixes (always explicitly check for NULL return). Bug report by Marian Stagarescu - Bug fix re. adding a local member with older ciscos (in =add_leaf()=). Bug report by Marian Stagarescu - Added explicit check whether =BYTE_ORDER= in pimd.h is defined. Bug report by * Version 2.1.0-alpha29.6: May 4, 2001 ** Bug Fixes - Bug fixes in processing Join/Prune messages. Thanks to Sri V * Version 2.1.0-alpha29.5: February 22, 2001 ** Changes & New Features - =VIFM_FORWARDER()= macro renamed to =VIFM_LASTHOP_ROUTER=. - Mini-FAQ entries added to README. ** Bug Fixes - When there is a new member, =add_leaf()= is called by IGMP code for any router, not only for a DR. The reason is because not only the DR must know about local members, but the last-hop router as well (so eventually it will initiate a SPT switch). Similar fixes to =add_leaf()= inside route.c as well. Problem reported by Hitoshi Asaeda . XXX: Note the lenghty comment in the beginning of =add_leaf()= about a pimd desing problem that may result in SPT switch not initiated immediately by the last-hop router. - DR entry timer bug fix in timer.c: When (*,G)'s iif and (S,G)'s iif are not same, (S,G)'s timer for the DR doesn't increase. Reported indirectly by * Version 2.1.0-alpha29.4: December 1, 2000 ** Changes & New Features - README cleanup + Mini-FAQ added - =igmp_proto.c=: printf argument cleanup (courtesy KAME) - =main.c:restart()=: forgotten printf argument added (courtesy KAME) ** Bug Fixes - =kern.c:k_stop_pim()=: Fix the ordering of =MRT_PIM= and =MRT_DONE=, thanks to Hitoshi Asaeda . - =route.c:add_leaf()=: mrtentry creation logic bug fix. If the router is not a DR, a mrtentry is never created. Tanks to Hitoshi Asaeda & (indirectly) - =pim_proto.c=: Two critical bug fixes. J/P prune suppression related message and J/P message with (*,*,RP) entry inside. Thanks to Azzurra Pantella and Nicola Dicosmo from University of Pisa - =pim_proto.c:receive_pim_bootstrap()=: BSR-related fix from Kame's pim6sd. Even when the BSR changes, just schedule an immediate advertisemnet of C-RP-ADV, instead of sending message, in order to avoid sending the advertisement to the old BSR. In response to comment from * Version 2.1.0-alpha29.3: October 13, 2000 ** Bug Fixes - =ADVANCE()= bug fix in routesock.c (if your system doesn't have =SA_LEN=) thanks to Eric S. Johnson * Version 2.1.0-alpha29.2: October 13, 2000 NB: THIS pimd VERSION WON'T WORK WITH OLDER PIM-SM KERNEL PATCHES (kernel patches released prior to this version)! ** Changes & New Features - The daemon that the kernel will prepare completely the inner multicast packet for PIM register messages that the kernel is supposed to encapsulate and send to the RP. - Now pimd compiles on OpenBSD-2.7. PIM control messages exchange test passed. Ddon't have the infrastructure to perform more complete testing. - =main.c:cleanup()=: Send =PIM_HELLO= with holdtime of '0' if pimd is going away, thanks to JINMEI Tatuya - =include/netinet/pim.h= updated - pimd code adapted to the new =struct pim= definition. - Added =PIM_OLD_KERNEL= and =BROKEN_CISCO_CHECKSUM= entries in the Makefile. - Don't ignore kernel signals if any of src or dst are NULL. - Don't touch =ip_id= on a PIM register message - README cleanup: kernel patches location, obsoleted systems clarification, etc. - =k_stop_pim()= added to =cleanup()= in =main.c= (courtesy Kame) ** Bug Fixes - =RANDOM()=-related bug fix re. =jp_value= calculation in =pim_proto.c=, thanks to JINMEI Tatuya - =realloc()= related memory leak bug in =config_vifs_from_kernel()= in config.c courtesy Kame's pim6sd code. - Solaris-8 fixes thanks to Eric S. Johnson - =BROKEN_CISCO_CHECKSUM= bug fix thanks to Eric S. Johnson and Hitoshi Asaeda. - =main.c=: 1000000 usec -> 1 sec 0 usec. Fix courtesy of the Kame project - =main.c:restart()= fixup courtesy of the Kame project - various min. message length check for the received control messages courtesy of the Kame project. XXX: the pimd check is not enough! - VIF name string comparison fix in =routesock.c:getmsg()= courtesy of the Kame project - missing brackets added inside =age_routes()= (a bug that will show up only if =KERNEL_MFC_WC_G= was defined); courtesy of the Kame project * Version 2.1.0-alpha28: March 15, 2000 ** Changes & New Features - added #ifdef =BROKEN_CISCO_CHECKSUM= (disabled by default) to make cisco RPs happy (read the comments in pim.c) - added #ifdef =PIM_TYPEVERS_DECL= in netinet/pim.h as a workaround that ANSI-C doesn't guarantee that bit-fields are tightly packed together (although all modern C compilers should not create a problem). ** Bug Fixes - Fixes to enable point-to-point interfaces being added correctly, thanks to Roger Venning - A number of minor bug fixes * Version 2.1.0-alpha27: January 21, 2000 NB: this release may the the last one from 2.1.0. The next release will be 2.2.0 and there will be lots of changes inside. ** Bug Fixes - Bug fix in =rp.c:add_grp_mask()= and =rp.c:delete_grp_mask()=: in some cases if the RPs are configured with nested multicast prefixes, the add/delete may fail. Thanks to Hitoshi Asaeda and the KAME team for pointing out this one. * Version 2.1.0-alpha26: October 28, 1999 ** Bug Fixes - Bug fix in =receive_pim_register()= in =pim_proto.c:ntohl()= was missing inside =IN_MULTICAST()=. Thanks to Fred Griffoul - Bug report and fix by Hitoshi Asaeda in =pim_proto.c:receive_pim_cand_rp_adv()= (if a router is not a BSR). Another bug in =rp.c:delete_grp_mask_entry()=: an entry not in the head of the list was not deleted propertly. - Some =VIFF_TUNNEL= checks added or deleted in various places. Slowly preparing pimd to be able to work with GRE tunnels... * Version 2.1.0-alpha25: August 30, 1999 Bug reports and fixes by Hitoshi Asaeda inside =parse_reg_threshold()= and =parse_data_threshold()= in config.c ** Changes & New Features - Successfully added multicast prefixes configured in pimd.conf are displayed at startup - Use =include/freebsd= as FreeBSD-3.x include files and =include/freebsd2= for FreeBSD-2.x. ** Bug Fixes - Test is performed whether a =PIM_REGISTER= has invalid source and/or group address of the internal packet. * Version 2.1.0-alpha24: August 9, 1999 ** Changes & New Features - =PIM_DEFAULT_CAND_RP_ADV_PERIOD= definition set to 60, but default 'time' value for inter Cand-RP messages is set in pimd.conf to 30 sec. - =PIM_REGISTER= checksum verification in =receive_pim_register()= relaxed for compatibility with some older routers. The checksum has to be computed only over the first 8 bytes of the PIM Register (i.e. only over the header), but some older routers might compute it over the whole packet. Hence, the checksum verification is over the first 8 bytes first, and if if it fails, then over the whole packet. Thus, pimd that is RP should still work with older routers that act as DR, but if an older router is the RP, then pimd cannot be the DR. Sorry, don't know which particular routers and models create the checksum over the whole PIM Register (if there are still any left). * Version 2.1.0-alpha23: May 24, 1999 ** Changes & New Features - Finally pimd works under Linux (probably 2.1.126, 2.2.x and 2.3.x). However, a small fix in the kernel =linux/net/ipv4/ipmr.c= is necessary. In function =pim_rcv()=, remove the call to =ip_compute_csum()=: #+BEGIN_SRC c --- linux/net/ipv4/ipmr.c.org Thu Mar 25 09:23:34 1999 +++ linux/net/ipv4/ipmr.c Mon May 24 15:42:45 1999 @@ -1342,8 +1342,7 @@ if (len < sizeof(*pim) + sizeof(*encap) || pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || (pim->flags&PIM_NULL_REGISTER) || - reg_dev == NULL || - ip_compute_csum((void *)pim, len)) { + reg_dev == NULL) { kfree_skb(skb); return -EINVAL; } #+END_SRC - in pimd.conf "phyint" can be specified not only by IP address, but by name too (e.g. "phyint de1 disable") - in pimd.conf 'preference' and 'metric' can be specified per "phyint" Note that these 'preference' and 'metric' are like per iif. - =MRT_PIM= used (again) instead of =MRT_ASSERT= in kern.c. The problem is that Linux has both =MRT_ASSERT= and =MRT_PIM=, while *BSD has only =MRT_ASSERT=. #+BEGIN_SRC c #ifndef MRT_PIM #define MRT_PIM MRT_ASSERT #endif #+END_SRC - Rely on =__bsdi__=, which is defined by the OS, instead of -DBSDI in Makefile, change by Hitoshi Asaeda. Similarly, use =__FreeBSD__= instead of -DFreeBSD - Linux patches by Fred Griffoul including a =netlink.c= instead of =routesock.c= - =vif.c:zero_vif()=: New function ** Bug Fixes All bug reports thanks to Kaifu Wu - Linux-related bug fixes regarding raw IP packets byte ordering - Join/Prune message bug fixed if the message contains several groups joined/pruned * Version 2.1.0-alpha22: November 11, 1998 Bug reports by Jonathan Day ** Bug Fixes - Bug fixes to compile under newer Linux kernel (linux-2.1.127) To compile for older kernels ( ver < ???), add =-Dold_Linux= to the Makefile - For convenience, the =include/linux/netinet/{in.h,mroute.h}= files are added, with few modifications applied. * Version 2.1.0-alpha21: November 4, 1998 ** Bug Fixes - =pim_proto.c:join_or_prune()=: Bug fixes in case of (S,G) overlapping with (*,G). Bug report by Dirk Ooms - =route.c:change_interfaces()=: Join/Prune (*,G), (*,*,RP) fire timer optimization/fix. * Version 2.1.0-alpha20: August 26, 1998 ** Changes & New Features - (Almost) all timers manipulation now use macros - =pim.h= and =pim_var.h= are in separate common directory - Added BSDI definition to =pim_var.h=, thanks to Hitoshi Asaeda. ** Bug Fixes - fix TIMEOUT definitions in difs.h (bug report by Nidhi Bhaskar) (originally, if timer value less than 5 seconds, it won't become 0) It is HIGHLY recommended to apply that fix, so here it is: #+BEGIN_SRC c -------------BEGIN BUG FIX------------------- 1) Add the following lines to defs.h (after #define FALSE): #ifndef MAX #define MAX(a,b) (((a) >= (b))? (a) : (b)) #define MIN(a,b) (((a) <= (b))? (a) : (b)) #endif /* MAX & MIN */ 2) Change the listed below TIMEOUT macros to: #define IF_TIMEOUT(timer) \ if (!((timer) -= (MIN(timer, TIMER_INTERVAL)))) #define IF_NOT_TIMEOUT(timer) \ if ((timer) -= (MIN(timer, TIMER_INTERVAL))) #define TIMEOUT(timer) \ (!((timer) -= (MIN(timer, TIMER_INTERVAL)))) #define NOT_TIMEOUT(timer) \ ((timer) -= (MIN(timer, TIMER_INTERVAL))) ---------------END BUG FIX------- #+END_SRC * Version 2.1.0-alpha19: July 29, 1998 Both bug reports by Chirayu Shah - ** Bug Fixes - bug fix in =find_route()= when searching for (*,*,RP) - bug fix in =move_kernel_cache()=: no need to do =move_kernel_cache()= from (*,*,R) to (*,G) first when we call =move_kernel_cache()= for (S,G) * Version 2.1.0-alpha18: May 29, 1998 ** Changes & New Features - Now compiles under Linux (haven't checked whether the PIMv2 kernel support in linux-2.1.103 works) ** Bug Fixes - =parse_default_source*()= bug fix (bug reports by Nidhi Bhaskar) - allpimrouters deleted from igmp.c (already defined in pim.c) - igmpmsg defined for IRIX * Version 2.1.0-alpha17: May 21, 1998 ** Changes & New Features - (*,G) MFC kernel support completed and verified. Compile with =KERNEL_MFC_WC_G= defined in Makefile, but then must use it only with a kernel that supports (*,G), e.g. =pimkern-PATCH_7=. Currently, kernel patches available for FreeBSD and SunOS only. ** Bug Fixes - =MRTF_MFC_CLONE_SG= flag set after =delete_single_kernel_cache()= is called * Version 2.1.0-alpha16: May 19, 1998 ** Changes & New Features - PIM registers kernel encapsulation support. Build with =PIM_REG_KERNEL_ENCAP= defined in Makefile. - (*,G) MFC support. Build with =KERNEL_MFC_WC_G= defined in Makefile. However, =MFC_WC_G= is still not supported with =pimkern-PATCH_6=, must disable it for now. - =mrt.c:delete_single_kernel_cache_addr()=: New function, uses source, group to specify an MFC to be deleted * Version 2.1.0-alpha15: May 14, 1998 - Another few bug fixes related to NetBSD definitions thanks to Heiko W.Rupp * Version 2.1.0-alpha14: May 12, 1998 - A few bug fixes related to NetBSD definitions thanks to Heiko W.Rupp * Version 2.1.0-alpha13: May 11, 1998 ** Changes & New Features - If the RP changes, the necessary actions are taken to pass the new RP address to the kernel. To be used for kernel register encap. support. Wnat needs to be done is: (a) add =rp_addr= entry to the mfcctl structure, and then just set it in =kern.c:k_chf_mfc()=. Obviously, the kernel needs to support the register encapsulation (instead of sending WHOLEPKT to the user level). In the near few days will make the necessary kernel changes. - =change_interfaces()=: Added "flags" argument. The only valid flag is =MFC_UPDATE_FORCE=, used for forcing kernel call when only the RP changes. - =k_chg_mfc()= has a new argument: rp_addr. To be used for kernel register encapsulation support - =MRT_PIM= completely replaced by =MRT_ASSERT= - =move_kernel_cache()=: Argument =MFC_MOVE_FORCE= is a flag instead of TRUE/FALSE - =process_cache_miss()=: removed unneeded piece of code * Version 2.1.0-alpha12: May 10, 1998 ** Changes & New Features - Use the cleaned up =netinet/pim.h= - Remove the no needed anymore pim header definition in =pimd.h= - Don't use =MRT_PIM= in in kern.c anymore, replaced back with =MRT_ASSERT=. - =added default_source_metric= and =default_source_preference= (1024) because the kernel's unicast routing table is not a good source of info; configurable in pimd.conf - Can now compile under NetBSD-1.3, thanks to Heiko W.Rupp ** Bug Fixes - Incorrect setup of the borderBit and nullRegisterBit (different for big and little endian machines) fixed; =*_BORDER_BIT= and =*NULL_REGISTER_BIT= redefined - don't send =pim_assert= on tunnels or register vifs (if for whatever reason we receive on such interface) - ignore =WRONGVIF= messages for register and tunnel vifs (the cleaned up kernel mods dont send such signal, but the older (before May 9 '98) pimd mods that signaling was enabled * Version 2.1.0-alpha11: March 16, 1998 ** Changes & New Features - =vif.c:find_vif_direct_local()=: New function, used in =routesock.c=, =igmp_proto.c= - Use =MFC_MOVE_FORCE/MFC_MOVE_DONT_FORCE= flag in =mrt.c=, =route.c=, =pim_proto.c=, when need to move the kernel cache entries between (*,*,RP), (*,G), (S,G) - new timer related macros: =SET_TIMER()=, =FIRE_TIMER()=, =IF_TIMER_SET()=, =IF_TIMER_NOT_SET()= ** Bug Fixes - =timer.c:age_routes()=: bunch of fixes regarding J/P message fragmentation - =route.c:process_wrong_iif()=: (S,G) SPT switch bug fix: ANDed =MRTF_RP= fixed to =MRTF_RP= - =pim_proto.c= & =timer.c=: (S,G) Prune now is sent toward RP, when iif toward S and iif toward RP are different - =pim_proto.c:join_or_prune()= bug fixes - =pim_proto.c=: (S,G)Prune entry's timer now set to J/P message holdtime - =pim_proto.c:receive_pim_join_prune()=: Ensure pruned interfaces are correctly reestablished - =timer.c:age_routes()=: now (S,G) entry with local members (inherited from (*,G)) is timeout propertly - =timer.c:age_routes()=: (S,G) J/P timer restarted propertly - =timer.c:age_routes()=: check also the (S,G)RPbit entries in the forwarders and RP and eventually switch to the shortest path if data rate too high - =route.c:process_wrong_vif()= fire J/P timer - =route.c:switch_shortest_path()=: reset the iif toward S if there is already (S,G)RPbit entry * Version 2.1.0-alpha10: March 3, 1998 Temp. non-public release. ** Changes & New Features - `interval` can be applied for data rate check. The statement in =pimd.conf= that only the default value will be used is not true anymore. - The RP-initiated and the forwarder-initiated (S,G) switch threshold rate can be different. - =pim_proto.c:receive_pim_register()=: check if I am the RP for that group, and if "no", send =PIM_REGISTER_STOP= (XXX: not in the spec, but should be!) - =pim_proto.c:receive_pim_register_stop()=: check if the =PIM_REGISTER_STOP= originator is really the RP, before suppressing the sending of the PIM registers. (XXX: not in the spec but should be there) - =rp.c:check_mrtentry_rp()=: new function added to check whether the RP address is the corresponding one for the given mrtentry - =debug.c:dump_mrt()= timer values added - =route.c=: =add_leaf()=, =process_cache_miss()=, =process_wrong_iif()= no routing entries created for the LAN scoped addresses - =DEBUG_DVMRP_DETAIL= and =DEBUG_PIM_DETAIL= added ** Bug Fixes - =mrt.c:add_kernel_cache()=: no kernel cache duplicates - =mrt.c:move_kernel_cache()=: if the iif of the (*,*,R) (or (*,G)) and (S,G) are different, dont move the cache entry "UP" - =timer.c:age_routes()=: (S,G) =add_jp_entry()= flag fixed, SPT switch related. - =kern.c:k_get_sg_cnt()=: modified to compensate for the kernel's return code bug for getting (S,G) byte count (=SIOCGETSGCNT=) - =pim_proto.c:receive_pim_register()=: if the (S,G) oif is NULL, now checks whether the iif is =register_vif= * Version 2.1.0-alpha9: February 18, 1997 ** Changes & New Features - "non-commersial" statement deleted from the copyright message - mrinfo support added - mtrace support added (not completed and not enough tested) - if invalid local address for =cand_rp= or =cand_bootstrap_router= in =pimd.conf=, automatically will use the largest local multicast enabled address - "include" directory for FreeBSD and SunOS added, so now pimd can be compiled without having the necesary "include" files added to your system. Probably a bad idea and may remove it later. - some default values for the IP headers of the IGMP and PIM packets are fixed - =VIFF_PIM_NBR= and =VIFF_DVMRP_NBR= flags added - =VIFF_REGISTER= now included in the RSRR vifs report - =find_route()= debug messages removed - #ifdef for =HAVE_SA_LEN= corrected - =debug.c=: small fixes * Version 2.1.0-alpha8: November 23, 1997 ** Bug Fixes - BSDI related bug fix in defs.h - small changes in Makefile * Version 2.1.0-alpha7: November 23, 1997 ** Changes & New Features - RSRR support for (*,G) completed - BSDI 3.0/3.1 support by Hitoshi Asaeda (the kernel patches will be available soon) - Improved debug messages format (thanks to Hitoshi Asaeda) - A new function =netname()= for network IP address print instead of =inet_fmts()=, thanks to Hitoshi Asaeda. - =pimd.conf=: format changed * Version 2.1.0-alpha6: November 20, 1997 ** Bug Fixes - Remove the inherited leaves from (S,G) when a receiver drops membership - some parameters when calling =change_interface()= fixed - use =send_pim_null_register= + take the appropriate action when the register suppression timer expires - bug fix related to choosing the largest local IP address for little endian machines. * Version 2.1.0-alpha5 ** Bug Fixes - =main.c:main()=: startup message fix - =timer.c:age_routes()=: bug fix in debug code * Version 2.1.0-alpha4: October 31, 1997 ** Changes & New Features - Minor changes, so pimd now compiles for SunOS 4.1.3 (cc, gcc) ** Bug Fixes - =pim_proto.csend_periodic_pim_join_prune()=: bug fix thanks to SunOS cc warning(!), only affects the (*,*,RP) stuff. - =pimd.conf=: two errors, related to the rate limit fixed * Version 2.1.0-alpha3: October 13, 1997 ** Changes & New Features - =Makefile=: cleanup - =defs.h=: cleanup - =routesock.c=: cleanup ** Bug Fixes - =igmp_proto.c:accept_group_report()=: bug fixes - =pim_proto.c:receive_pim_hello()=: bug fixes - =route.c:change_interfaces()=: bug fixes - =rp.c=: bug fixes in =init_rp_and_bsr()=, =add_cand_rp()=, and =create_pim_bootstrap_message()= * Version 2.1.0-alpha2: September 23, 1997 ** Changes & New Features - =Makefile=: "make diff" code added - =debug.c=: debug output slightly changed ** Bug Fixes - =defs.h:*TIMEOUT()=: definitions fixed - =route.c=: bugs fixed in =change_interface()= and =switch_shortest_path()= - =timer.c:age_routes()=: number of bugs fixed * Version 2.1.0-alpha1: August 26, 1997 ** Changes & New Features First alpha version of the "new, up to date" pimd. RSRR support + Solaris support added. Many functions rewritten and/or modified. # Local Variables: # mode: org # End: pimd-2.3.2/FAQ.md000066400000000000000000000073601267035112600134110ustar00rootroot00000000000000Mini FAQ ======== * Q: My RP is a Cisco router, but it doesn't work with pimd? If your Cisco is running PIM-SMv1, it won't work with pimd which implements only PIM-SMv2. You need to upgrade/configure your Cisco to run PIM-SMv2. If your Cisco is indeed running PIM-SMv2, and it is the RP, you need to run the pimd `configure` script with `--enable-broken-crc` defined. See the beginning of the configure script, or the output from the command `configure --help`. Note that this will then likely cause the PIM Register messages to *not* be accepted by some other vendors, but pimd-to-pimd should still be OK. **Note:** This is a *very* old FAQ and this issue is exteremly likely to be rather reversed in 2015 ... * Q: Do I need to re-configure my Linux kernel to run pimd? Maybe, most major GNU/Linux distributions today ship with multicast capable Linux kernels. However, do make a habit of verifying that you have at least the following: CONFIG_IP_MULTICAST CONFIG_IP_PIMSM_V2 CONFIG_IP_MROUTE You *may* enable `CONFIG_IP_PIMSM_V1` as well, but it is likely not required to interop with any active equipment anymore. What may cause you to have to recompile Linux in 2015 is the lack of multiple multicast routing tables. My Ubuntu 15.04 lists the following in its `/boot/config-3.19.0-23-generic`: # CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set Also, make sure to check that NETLINK related settings are enabled, because that is the interface pimd uses on Linux, not routing sockets anymore. Again, very likely to be default in 2015. Make sure that those options are set to "y" to include the relevant code in the kernel; if you enable them as modules, then you may have to load that module after you boot with the new kernel. One way to find-out if multicast routing is not working, is to use command `cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding` after you have started pimd (you may use other interface name instead of `eth0`). If it returns zero, multicast forwarding on that interface is not working. * Q: I tried pimd on Linux, but I get the following error message: netlink socket: Address family not supported by protocol You need to enable the NETLINK related stuff in the kernel and recompile it. * Q: pimd compiled and is running on a single machines, but when I run it on 2+ machines, the multicast packets do not reach the receivers. Without detailed debug information I cannot answer this question. Please send to the pimd maintainer a scheme (topology map) of your network, and the debug output from each router (`pimd -dall`), that may help. * Q: How do I debug my multicast routing? Check [README-debug.md][debug] for some hints. * Q: How do I use pimd with GRE tunnels? See the file [README-config.md][config] for examples. * Q: How do I run pimd but without configuring it as a Cand-RP and/or a Cand-BSR? See the file [README-config.md][config] for details. * Q: I have set the `phyint dr-priority` to 10, but another router is still elected as DR, why? This happens when not all routers on a LAN advertise the *DR Priority* option in PIM Hello messages. Check with tcpdump or wireshark to find the culprit. Versions of pimd older than v2.3.0 did not support the *DR Priority* option. * Q: How do I configure pimd to do FOO? See file [README-config.md][config]. If the answer is not there, send an email to the current pimd maintainer, or file a bug report at the [GitHub issue tracker][tracker]. [debug]: https://github.com/troglobit/pimd/blob/dev/README-debug.md [config]: https://github.com/troglobit/pimd/blob/dev/README-config.md [tracker]: https://github.com/troglobit/pimd/issues pimd-2.3.2/INSTALL.md000066400000000000000000000067421267035112600141130ustar00rootroot00000000000000Installation instruction for pimd ================================= It is recommended to use a pimd from your distribution, be it from ports in one of the major BSD's, or your GNU/Linux distribution of choice. However, if you want to try the latest bleeding edge pimd, download one of the release tarballs at After unpacking the tarball, cd to the new directory, e.g. `pimd-2.3.0/` followed by: ./configure && make sudo make install By default pimd is installed to the `/usr/local` prefix, except for `pimd.conf` which is installed to `/etc`. If you want to install to another directory, e.g. `/opt`, use: ./configure --prefix=/opt && make sudo make install This will change both the `--prefix` and the `--sysconfdir` paths. To install `pimd.conf` to another path, add `--sysconfdir` *after* the `--prefix` path. For distribution packagers and ports maintainers, the pimd `Makefile` supports the use of `DESTDIR=` to install to a staging directory. What you want is probably something like: ./configure --prefix=/usr --sysconfdir=/etc && make make DESTDIR=/tmp/staging VERSION=2.3.0-1 install The default `/etc/pimd.conf` should be good enough for most use cases. But if you edit it, see the man page or the comments in the file for some help. NetBSD and FreeBSD users may have to install the kernel modules to get multicast routing support, including PIM support. See your respective documentation, or consult the web for help! Cross Compiling --------------- The pimd build system does not use GNU autotools, but it is still possible to cross-compile. Simply make sure to give the `configure` script the correct paths and options, and then set the environment variable `CROSS` to your cross compiler prefix. E.g. ./configure --prefix=/ --embedded-libc make CROSS=arm-linux-gnueabi- **Note:** some toolchains do not properly setup at `cc` symlink, for instance the Debian/Ubuntu ARM toolchains. Instead they assume that projects are using GCC and only provide a `gcc` symlink. Old INSTALL ----------- Old install instructions, before PIM kernel support was readily available in all major operating systems 1. Apply the PIM kernel patches, recompile, reboot 2. Copy pimd.conf to /etc and edit as appropriate. Disable the interfaces you don't need. Note that you need at least 2 physical interfaces enabled. 3. Edit Makefile by uncommenting the line(s) corresponding to your platform. 4. Recompile pimd 5. Run pimd as a root. It is highly recommended to run it in debug mode. Because there are many debug messages, you can specify only a subset of the messages to be printed out: usage: pimd [-c configfile] [-d [debug_level][,debug_level]] Valid debug levels: `dvmrp_prunes`, `dvmrp_mrt`, `dvmrp_neighbors`, `dvmrp_timers`, `igmp_proto`, `igmp_timers`, `igmp_members`, `trace`, `timeout`, `pkt`, `interfaces`, `kernel`, `cache`, `rsrr`, `pim_hello`, `pim_register`, `pim_join_prune`, `pim_bootstrap`, `pim_asserts`, `pim_cand_rp`, `pim_routes`, `pim_timers`, `pim_rpf` If you want to see all messages, use `pimd -dall` only. 6. Note that it takes of the order of 30 seconds to 1 minute until the Bootstrap router is elected and the RP-set distributed to the PIM routers, and without the RP-set in the routers the multicast packets cannot be forwarded. 7. There are plenty of bugs, some of them known (check BUGS.TODO), some of them unknown, so your bug reports are more than welcome. pimd-2.3.2/LICENSE000066400000000000000000000036411267035112600134630ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: LICENSE,v 1.5 2001/09/10 20:31:36 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ pimd-2.3.2/LICENSE.mrouted000066400000000000000000000032441267035112600151400ustar00rootroot00000000000000Copyright © 2002 The Board of Trustees of the Leland Stanford Junior University Permission is hereby granted to STANFORD's rights, free of charge, to any person obtaining a copy of this Software and associated documentation files ( "MROUTED"), to deal in MROUTED without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of MROUTED , and to permit persons to whom MROUTED is furnished to do so, subject to the following conditions: 1) The above copyright notice and this permission notice shall be included in all copies or substantial portions of the MROUTED . 2) Neither the STANFORD name nor the names of its contributors may be used in any promotional advertising or other promotional materials to be disseminated to the public or any portion thereof nor to use the name of any STANFORD faculty member, employee, or student, or any trademark, service mark, trade name, or symbol of STANFORD or Stanford Hospitals and Clinics, nor any that is associated with any of them, without STANFORD's prior written consent. Any use of STANFORD's name shall be limited to statements of fact and shall not imply endorsement of any products or services. 3) MROUTED IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH MROUTED OR THE USE OR OTHER DEALINGS IN THE MROUTED . pimd-2.3.2/Makefile000066400000000000000000000067151267035112600141230ustar00rootroot00000000000000# -*-Makefile-*- # # Note: We use .gz for distribution archives for the sake of # OpenBSD not having bzip2 in the base distribution. # #VERSION = $(shell git tag -l | tail -1) VERSION = 2.3.2 EXEC = pimd CONFIG = $(EXEC).conf PKG = $(EXEC)-$(VERSION) ARCHTOOL = `which git-archive-all` ARCHIVE = $(PKG).tar ARCHIVEZ = ../$(ARCHIVE).gz ROOTDIR ?= $(dir $(shell pwd)) RM = rm -f CC := $(CROSS)$(CC) CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all IGMP_OBJS = igmp.o igmp_proto.o trace.o ROUTER_OBJS = inet.o kern.o main.o config.o debug.o vers.o callout.o PIM_OBJS = route.o vif.o timer.o mrt.o pim.o pim_proto.o rp.o DVMRP_OBJS = dvmrp_proto.o # This magic trick looks like a comment, but works on BSD PMake #include include config.mk prefix ?= /usr/local sysconfdir ?= /etc datadir = $(prefix)/share/doc/pimd mandir = $(prefix)/share/man/man8 ## Common CPPFLAGS += -D_PATH_SYSCONF=\"$(sysconfdir)\" CFLAGS += $(INCLUDES) $(DEFS) $(USERCOMPILE) CFLAGS += -W -Wall -Wextra -fno-strict-aliasing LDLIBS = $(EXTRA_LIBS) OBJS = $(IGMP_OBJS) $(ROUTER_OBJS) $(PIM_OBJS) $(DVMRP_OBJS) $(EXTRA_OBJS) SRCS = $(OBJS:.o=.c) MANS = $(EXEC).8 DISTFILES = README.md README-config.md README.config.jp README-debug.md ChangeLog.org \ CONTRIBUTING.md CODE-OF-CONDUCT.md INSTALL.md LICENSE LICENSE.mrouted \ TODO.org CREDITS FAQ.md AUTHORS all: $(EXEC) .c.o: @printf " CC $@\n" @$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< $(EXEC): $(OBJS) @printf " LINK $@\n" @$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $(OBJS) $(LDLIBS) vers.c: @echo $(VERSION) | sed -e 's/.*/char todaysversion[]="&";/' > vers.c install: $(EXEC) @install -d $(DESTDIR)$(prefix)/sbin @install -d $(DESTDIR)$(sysconfdir) @install -d $(DESTDIR)$(datadir) @install -d $(DESTDIR)$(mandir) @install -m 0755 $(EXEC) $(DESTDIR)$(prefix)/sbin/$(EXEC) @install -b -m 0644 $(CONFIG) $(DESTDIR)$(sysconfdir)/$(CONFIG) @for file in $(DISTFILES); do \ install -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \ done @for file in $(MANS); do \ install -m 0644 $$file $(DESTDIR)$(mandir)/$$file; \ done uninstall: -@$(RM) $(DESTDIR)$(prefix)/sbin/$(EXEC) -@$(RM) $(DESTDIR)$(sysconfdir)/$(CONFIG) -@$(RM) -r $(DESTDIR)$(datadir) @for file in $(DISTFILES); do \ $(RM) $(DESTDIR)$(datadir)/$$file; \ done -@for file in $(MANS); do \ $(RM) $(DESTDIR)$(mandir)/$$file; \ done clean: -@$(RM) $(OBJS) $(EXEC) distclean: -@$(RM) $(OBJS) core $(EXEC) vers.c config.mk tags TAGS *.o *.map .*.d *.out tags dist: @if [ x"$(ARCHTOOL)" = x"" ]; then \ echo "Missing git-archive-all from https://github.com/Kentzo/git-archive-all"; \ exit 1; \ fi @if [ -e $(ARCHIVEZ) ]; then \ echo "Distribution $(ARCHIVEZ) already exists."; \ exit 1; \ fi @echo "Building gz tarball of $(PKG) in parent dir ..." @$(ARCHTOOL) ../$(ARCHIVE) @gzip ../$(ARCHIVE) @md5sum $(ARCHIVEZ) | sed 's/..\///' | tee $(ARCHIVEZ).md5 build-deb: @echo "Building .deb if $(PKG)..." git-buildpackage --git-ignore-new --git-upstream-branch=master check lint: $(CHECK) *.c *.h tags: $(SRCS) @ctags $(SRCS) cflow: @cflow $(MCAST_INCLUDE) $(SRCS) > cflow.out cflow2: @cflow -ix $(MCAST_INCLUDE) $(SRCS) > cflow2.out rcflow: @cflow -r $(MCAST_INCLUDE) $(SRCS) > rcflow.out rcflow2: @cflow -r -ix $(MCAST_INCLUDE) $(SRCS) > rcflow2.out TAGS: @etags $(SRCS) pimd-2.3.2/README-config.md000066400000000000000000000114031267035112600151730ustar00rootroot00000000000000> $Id: README.config,v 1.4 2002/06/13 17:39:19 pavlin Exp $ This is file contains help for configuring and using pimd, the PIM-SM/SSM multicast daemon. For the latest pimd version, see There is an older Japanese version of this file, it could need some updating help, in the meantime, see [README.config.jp][jp] **NOTE:** currently, this file is very incomplete. If something is missing and/or unclear, email the current maintainer of pimd or file an issue in the GitHub issue tracker. ## Using GRE Tunnels for Multicast Routing Based on information contributed by Hiroyuki Komatsu If you are configuring the particular gre interfaces for the first time, ignore the errors after `ip link set gre1 down` and `ip tunnel del gre1` On Linux (Debian) try the following: ### GRE Tunnel Between Two Machines This sets up a GRE tunnel between hosts 11.11.11.11 and 33.33.33.33. Physical interfaces: [11.11.11.11] [33.33.33.33] GRE tunnel: 22.22.22.11 <-------> 22.22.22.33 ==== host 11.11.11.11 (GRE interface 22.22.22.11) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip link set gre1 down ip tunnel del gre1 ip tunnel add gre1 mode gre remote 33.33.33.33 local 11.11.11.11 ttl 127 ip addr add 22.22.22.11/24 peer 22.22.22.33/24 dev gre1 ip link set gre1 up multicast on ==== host 33.33.33.33 (GRE interface 22.22.22.33) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip link set gre1 down ip tunnel del gre1 ip tunnel add gre1 mode gre remote 11.11.11.11 local 33.33.33.33 ttl 127 ip addr add 22.22.22.33/24 peer 22.22.22.11/24 dev gre1 ip link set gre1 up multicast on ### GRE Tunnels with Three Machines > STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!! > > IF YOU ADD MORE THAN TWO GRE TUNNELS IN A CHAIN, IT IS VERY EASY TO CREATE > UNICAST ROUTING LOOPS, AND THIS MAY LEAD TO MULTICAST ROUTING LOOPS. > MULTICAST ROUTING LOOP IS A DISASTER THAT MAY BRING YOUR WHOLE NETWORK DOWN. > BEFORE ATTEMPTING THIS CONFIGURATION, MAKE SURE YOU UNDERSTAND VERY WELL > WHAT YOU ARE DOING, AND WHAT MAY HAPPEN. > IF YOU ARE READING THIS, THE CHANCES ARE THAT YOU DON'T KNOW, SO THINK AGAIN!! > > THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!! Physical interfaces: [11.11.11.11] [33.33.33.33] [55.55.55.55] GRE tunnels: 22.22.22.11 <--> 22.22.22.33 44.44.44.33 <--> 44.44.44.55 ==== host 33.33.33.33 (GRE interfaces 22.22.22.33 and 44.44.44.33) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 11.11.11.11 local 33.33.33.33 ttl 127 ip addr add 22.22.22.33/24 peer 22.22.22.11/24 dev gre1 ip link set gre1 up multicast on ip tunnel add gre2 mode gre remote 55.55.55.55 local 33.33.33.33 ttl 127 ip addr add 44.44.44.33/24 peer 44.44.44.55/24 dev gre2 ip link set gre2 up multicast on ==== host 55.55.55.55 (GRE interface 44.44.44.55) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 33.33.33.33 local 55.55.55.55 ttl 127 ip addr add 44.44.44.55/24 peer 44.44.44.33/24 dev gre1 ip link set gre1 up multicast on route add -net 22.22.22.0 netmask 255.255.255.0 gw 44.44.44.33 gre1 ==== host 11.11.11.11 (GRE interface 22.22.22.11) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 33.33.33.33 local 11.11.11.11 ttl 127 ip addr add 22.22.22.11/24 peer 22.22.22.33/24 dev gre1 ip link set gre1 up multicast on route add -net 44.44.44.0 netmask 255.255.255.0 gw 22.22.22.33 gre1 ## The pimd.conf FAQ For a complete list of all available options, see `pimd.conf` and the man page. 1. How to disable pimd being Cand-RP? Comment-out the `rp-candidate` and `group-prefix` lines in `pimd.conf` 2. How to disable pimd being Cand-BSR? Comment-out the `bsr-candidate` line in `pimd.conf` 3. How to prevent a prefix of multicast addresses being routed through my multicast router? If you want to scope, say, prefixes 238.0.0.0/8 and 239.0.0.0/8, add the following lines to `pimd.conf`: phyint eth1 scoped 238.0.0.0 masklen 8 phyint eth1 scoped 239.0.0.0 masklen 8 4. How to create a scope zone and stop multicast packets for some multicast prefix being propagated beyond the boundary of my network? Add scoping filters on your border routers for each prefix you want to scope. E.g.: phyint eth1 scoped 239.0.0.0 masklen 8 [jp]: https://github.com/troglobit/pimd/blob/master/README.config.jp pimd-2.3.2/README-debug.md000066400000000000000000000056401267035112600150220ustar00rootroot00000000000000> $Id: README.debug,v 1.4 2002/11/17 20:01:31 pavlin Exp $ This file contains some hints how to debug your multicast routing. **NOTE:** currently, it is very incomplete. If something is missing and/or unclear, email to the current maintainer of pimd or file an issue in the GitHub issue tracker. 1. Make sure that the TTL of the sender is large enough to reach the receiver. E.g., if the sender and the receiver are separated by a two routers in the middle, the TTL of the data packets transmitted by the sender must be at least 3. 2. Make sure the receiver sends IGMP join (membership report) for the group(s) it wants to receive. Use tcpdump on the router closest to the receiver to make sure. Sometimes buggy IGMP snooping switches, or cloud provider networks, filter out multicast in general, or all control traffic (IGMP/PIM) in particular. 3. Before you start the multicast routing daemon, verify the kernel config, the following settings should be activated: - On Linux: CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_IP_MROUTE_MULTIPLE_TABLES=y # Optional Check the list of multicast capable interfaces: cat /proc/net/dev_mcast - On *BSD: options MROUTING # Multicast routing options PIM # Enable for pimd - Start the multicast routing daemon in debug mode. E.g., `pimd -dall` or if you just want to see some subystems: `pimd -drpf,mrt -s7` 4. After you start the multicast routing daemon - Are the multicast vifs correctly installed in the kernel: - On Linux: cat /proc/net/ip_mr_vif - On *BSD: netstat -gn - Is multicast forwarding enabled on those vifs: - On Linux: sysctl net.ipv4.conf.eth0.mc_forwarding For each of the enabled interfaces. If it returns zero, the multicast forwarding on that interface is not working. - On *BSD: sysctl net.inet.ip.forwarding sysctl net.inet.ip.mforwarding # Only OpenBSD - Is the PIM multicast routing daemon exchanging `PIM_HELLO` messages with its neighbors? Look into the debug messages output; if necessary, use `tcpdump` as well. - Are the Bootpstrap messages received by all PIM-SM daemons? - If a Bootstrap message is received, is it accepted, or is it rejected because of a wrong iif? - After a while, does the Cand-RP set contain the set of RPs? - After the first multicast packets are received, is there `CACHE_MISS` signal from the kernel to the user-level daemon? - After the `CACHE_MISS` signal, are the MFC (Multicast Forwarding Cache) entries installed in the kernel? - On Linux: cat /proc/net/ip_mr_cache - On *BSD: netstat -gn pimd-2.3.2/README.config.jp000066400000000000000000000124121267035112600152060ustar00rootroot00000000000000=============================================================== $Id: README.config.jp,v 1.3 2002/06/13 17:39:19 pavlin Exp $ ¤³¤Îʸ½ñ¤Ï pimd ¤Î README.config.jp ¤Ç¤¹. pimd ¤Ï PIM-SM ¥Ð¡¼¥¸¥ç¥ó 2 ¤Î ¥Þ¥ë¥Á¥Á¥ã¥¹¥È¥Ç¡¼¥â¥ó¤Ç¤¹. ºÇ¿·ÈÇ¤Ï http://netweb.usc.edu/pim/pimd/ ¤Ç ³Îǧ¤·¤Æ¤¯¤À¤µ¤¤. ¤³¤Î¥Õ¥¡¥¤¥ë¤Ï pimd ¤ÎÀßÄê¤Ë¤Ä¤¤¤Æµ­½Ò¤·¤Æ¤¤¤Þ¤¹. ±Ñ¸ìÈÇ¤Ï README.config ¤Ë¤Ê¤ê¤Þ¤¹. XXX: ¸½¾õ¤Ç¤Ï, ¤³¤Î¥Õ¥¡¥¤¥ë¤Ï¤Þ¤ÀÉÔ´°Á´¤Ç¤¹. ¤â¤·¤Ê¤Ë¤«¤·¤éÈ´¤±¤Æ¤¤¤ë¤È¤³¤í¤ä ¤è¤¯Ê¬¤«¤é¤Ê¤¤¤È¤³¤í¤¬¤¢¤ê¤Þ¤·¤¿¤é, ºî¼Ô¤Ë¥á¡¼¥ë¤ò¤¯¤À¤µ¤¤. (¥á¡¼¥ë¥¢¥É¥ì¥¹¤Ï README ¥Õ¥¡¥¤¥ë¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤). 1. ¥Þ¥ë¥Á¥­¥ã¥¹¥È¥ë¡¼¥Æ¥£¥ó¥°¤ò¹Ô¤¦¤¿¤á¤Î GRE ¥È¥ó¥Í¥ë¤ÎÀßÄê (¾®¾¾¹°¹¬¤µ¤ó ¤Î¾ðÊó¤ò¸µ¤Ë¤·¤Æ¤¤¤Þ¤¹). Linux (Debian) ¤Î¾ì¹ç, °Ê²¼¤Î¤è¤¦¤Ë»î¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤: (½é¤á¤Æ GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¾ì¹ç, "ip link set gre1 down" ¤È "ip tunnel del gre1" ¤Î¸å¤Ë¥¨¥é¡¼¤¬½Ð¤Þ¤¹¤¬, ̵»ë¤·¤Æ²¼¤µ¤¤). 1.1 Æó¤Ä¤Î¥Þ¥·¥ó´Ö¤Ç¤Î GRE ¥È¥ó¥Í¥ë (¥Û¥¹¥È¤Ï 11.11.11.11 ¤È 33.33.33.33) ¼ÂºÝ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹: [11.11.11.11] [33.33.33.33] GRE ¥È¥ó¥Í¥ë: 22.22.22.11<--------->22.22.22.33 ==== ¥Û¥¹¥È 11.11.11.11 (GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ 22.22.22.11) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip link set gre1 down ip tunnel del gre1 ip tunnel add gre1 mode gre remote 33.33.33.33 local 11.11.11.11 ttl 127 ip addr add 22.22.22.11/24 peer 22.22.22.33/24 dev gre1 ip link set gre1 up multicast on ==== ¥Û¥¹¥È 33.33.33.33 (GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ 22.22.22.33) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip link set gre1 down ip tunnel del gre1 ip tunnel add gre1 mode gre remote 11.11.11.11 local 33.33.33.33 ttl 127 ip addr add 22.22.22.33/24 peer 22.22.22.11/24 dev gre1 ip link set gre1 up multicast on 1.2 »°¤Ä¤Î¥Þ¥·¥ó´Ö¤Ç¤Î GRE ¥È¥ó¥Í¥ë = ½ÅÍ× =============================================================== Æó¤Ä°Ê¾å¤Î GRE ¥È¥ó¥Í¥ë¤ò¿ô¼î·Ò¤®¤Ë¤·¤è¤¦¤¹¤ë¤È, Èó¾ï¤Ë¥æ¥Ë¥­¥ã¥¹¥È ¥ë¡¼¥Æ¥£¥ó¥°¤Î¥ë¡¼¥×¤òȯÀ¸¤µ¤»¤Æ¤·¤Þ¤¤¤ä¤¹¤¯¤Ê¤ê¤Þ¤¹. ¤³¤ì¤Ï¥Þ¥ë¥Á¥­¥ã¥¹¥È ¥ë¡¼¥Æ¥£¥ó¥°¤Î¥ë¡¼¥×¤â¤Þ¤¿°ú¤­µ¯¤³¤·¤Æ¤·¤Þ¤¤¤Þ¤¹. ¥Þ¥ë¥Á¥­¥ã¥¹¥È¥ë¡¼¥Æ¥£¥ó¥°¤Î ¥ë¡¼¥×¤Ï¥Í¥Ã¥È¥ï¡¼¥¯Á´ÂΤò¥À¥¦¥ó¤µ¤»¤Æ¤·¤Þ¤¦²ÄǽÀ­¤¬¤¢¤ë¤Û¤É¤Î¾ã³²¤Ç¤¹. °Ê²¼¤ÎÀßÄê¤ò»î¤·¤Æ¤ß¤ëÁ°¤Ë, ¡Ö¼«Ê¬¤¬²¿¤ò¤·¤Æ¤¤¤ë¤Î¤«¡×¤ª¤è¤Ó¡Ö¤½¤Î·ë²Ì ²¿¤¬µ¯¤³¤ë¤Î¤«¡×¤ò¤è¤¯Íý²ò¤·¤Æ¤¯¤À¤µ¤¤. ¤³¤Îʸ½ñ¤òÆÉ¤ó¤Ç¤¤¤ë¤È¤¤¤¦¤³¤È¤Ï, ¤Þ¤À´°Á´¤Ë¤ÏÍý²ò¤µ¤ì¤Æ¤¤¤Ê¤¤¤È¤¤¤¦ ¤³¤È¤«¤â¤·¤ì¤Þ¤»¤ó. ¤â¤¦°ìÅÙ¿¶¤êÊ֤äƹͤ¨¤Æ¤ß¤Æ¤¯¤À¤µ¤¤. ====================================================================== STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!!STOP!!! IF YOU ADD MORE THAN TWO GRE TUNNELS IN A CHAIN, IT IS VERY EASY TO CREATE UNICAST ROUTING LOOPS, AND THIS MAY LEAD TO MULTICAST ROUTING LOOPS. MULTICAST ROUTING LOOP IS A DISASTER THAT MAY BRING YOUR WHOLE NETWORK DOWN. BEFORE ATTEMPTING THIS CONFIGURATION, MAKE SURE YOU UNDERSTAND VERY WELL WHAT YOU ARE DOING, AND WHAT MAY HAPPEN. IF YOU ARE READING THIS, THE CHANCES ARE THAT YOU DON'T KNOW, SO THINK AGAIN!! THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!!THINK!!! ¼ÂºÝ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹: [11.11.11.11] [33.33.33.33] [55.55.55.55] GRE ¥È¥ó¥Í¥ë: 22.22.22.11 <--> 22.22.22.33 44.44.44.33 <--> 44.44.44.55 ==== ¥Û¥¹¥È 33.33.33.33 (GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ 22.22.22.33 and 44.44.44.33) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 11.11.11.11 local 33.33.33.33 ttl 127 ip addr add 22.22.22.33/24 peer 22.22.22.11/24 dev gre1 ip link set gre1 up multicast on ip tunnel add gre2 mode gre remote 55.55.55.55 local 33.33.33.33 ttl 127 ip addr add 44.44.44.33/24 peer 44.44.44.55/24 dev gre2 ip link set gre2 up multicast on ==== ¥Û¥¹¥È 55.55.55.55 (GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ 44.44.44.55) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 33.33.33.33 local 55.55.55.55 ttl 127 ip addr add 44.44.44.55/24 peer 44.44.44.33/24 dev gre1 ip link set gre1 up multicast on route add -net 22.22.22.0 netmask 255.255.255.0 gw 44.44.44.33 gre1 ==== ¥Û¥¹¥È 11.11.11.11 (GRE ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ 22.22.22.11) echo 1 > /proc/sys/net/ipv4/ip_forward echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter ip tunnel add gre1 mode gre remote 33.33.33.33 local 11.11.11.11 ttl 127 ip addr add 22.22.22.11/24 peer 22.22.22.33/24 dev gre1 ip link set gre1 up multicast on route add -net 44.44.44.0 netmask 255.255.255.0 gw 22.22.22.33 gre1 2. pimd ¤ÎÀßÄê »ÈÍѲÄǽ¤Ê¤¹¤Ù¤Æ¤Î¥ª¥×¥·¥ç¥ó¤Î°ìÍ÷¤Ï pimd.conf ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤. 2.1. pimd ¤¬ Cand-RP ¤Ë¤Ê¤é¤Ê¤¤¤è¤¦¤Ë¤¹¤ë¤Ë¤Ï? "rp-candidate" ¤È "group-prefix" ¤Î¹Ô¤ò¥³¥á¥ó¥È¥¢¥¦¥È¤·¤Æ¤¯¤À¤µ¤¤. 2.2. pimd ¤¬ Cand-BSR ¤Ë¤Ê¤é¤Ê¤¤¤è¤¦¤Ë¤¹¤ë¤Ë¤Ï? "bsr-candidate" ¤Î¹Ô¤ò¥³¥á¥ó¥È¥¢¥¦¥È¤·¤Æ¤¯¤À¤µ¤¤. 2.3. ¼«Ê¬¤Î¥Þ¥ë¥Á¥­¥ã¥¹¥È¥ë¡¼¥¿¤ò·Ðͳ¤¹¤ë¥Þ¥ë¥Á¥­¥ã¥¹¥È¥¢¥É¥ì¥¹¤Î ¤¢¤ë¥×¥ê¥Õ¥£¥Ã¥¯¥¹¤ò¼×¤ë¤Ë¤Ï? Î㤨¤Ð, ¥×¥ê¥Õ¥£¥Ã¥¯¥¹ 238.0.0.0/8 ¤È 239.0.0.0/8 ¤ò¥¹¥³¡¼¥×¤·¤¿¤¤¤Ê¤é, pimd.conf ¤Ë°Ê²¼¤Î¹Ô¤òÄɲ䷤Ƥ¯¤À¤µ¤¤: phyint eth1 scoped 238.0.0.0 masklen 8 phyint eth1 scoped 239.0.0.0 masklen 8 2.4. ¥¹¥³¡¼¥×ÈϰϤòºîÀ®¤·¤Æ, ¼«Ê¬¤Î¥Í¥Ã¥È¥ï¡¼¥¯¶­³¦¤ò±Û¤¨¤ÆÅÁÇŤµ¤ì¤ë ¥Þ¥ë¥Á¥­¥ã¥¹¥È¥×¥ê¥Õ¥£¥Ã¥¯¥¹¤Î¥Ñ¥±¥Ã¥È¤ò»ß¤á¤ë¤Ë¤Ï? ¥¹¥³¡¼¥×¤·¤¿¤¤¸Ä¡¹¤Î¥×¥ê¥Õ¥£¥Ã¥¯¥¹¤ËÂФ¹¤ë¼«Ê¬¤Î¶­³¦¥ë¡¼¥¿¤Ë ¥¹¥³¡¼¥Ô¥ó¥°¥Õ¥£¥ë¥¿¤ò²Ã¤¨¤Æ¤¯¤À¤µ¤¤. Î㤨¤Ð: phyint eth1 scoped 239.0.0.0 masklen 8 pimd-2.3.2/README.md000066400000000000000000000267371267035112600137500ustar00rootroot00000000000000README ====== [![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan] Table of Contents ----------------- * [Introduction](#introduction) * [Download](#download) * [Building](#building) * [Configuration](#configuration) * [Example](#example) * [Starting](#starting) * [Monitoring](#monitoring) * [Contributing](#contributing) Introduction ------------ pimd is a lightweight, stand-alone PIM-SM/SSM multicast routing daemon available under the free [3-clause BSD license][BSD license]. This is the restored original version from University of Southern California, by Ahmed Helmy, Rusty Eddy and Pavlin Ivanov Radoslavov. Today pimd is [maintained at GitHub][GitHub]. Use its facilities to access the source, report bugs and feature requests, and send patches or pull requests. Official release tarballs at [the homepage][homepage]. pimd is primarily developed on Linux and should work as-is out of the box on all major distributions. Other UNIX variants (OpenBSD, NetBSD, and FreeBSD) should also work, but are not as thoroughly tested. For some tips and details, see the `configure` script. For a summary of changes for each release, see the [ChangeLog][changes]. Download -------- Although the project makes heavy use of GitHub, it is *not* recommended to use the ZIP file links GitHub provides. Instead, we recommend using proper tarball releases from [the FTP][], or the [releases page][]. The GitHub *Download ZIP* links, and ZIP files on the [releases page][], do not include files from the GIT submodules. The configure script has a check for this, but is not 100% foolproof. See below if you want to contribute. Building -------- When building pimd from source you first need to run the `configure` script to generate the file `config.mk`. The script relies on Bourne shell standard features as well as expr and uname. Any optional pimd features, such as `--enable-scoped-acls` are activated here as well. **Example:** ./configure --enable-scoped-acls && make sudo make install The configure script and Makefile supports de facto standard settings and environment variables such as `--prefix=PATH` and `DESTDIR=` for the install process. E.g., to install pimd to `/usr` instead of the default `/usr/local`, but redirect to a binary package directory in `/tmp`: ./configure --prefix=/usr && make clean all make VERSION=2.3.0-1 DESTDIR=/tmp/pimd-2.3.0-1 install Configuration ------------- The configuration is kept in the file `/etc/pimd.conf`, the order of the statements are in some cases important. PIM-SM is a designed to be a *protocol independent* multicast routing protocol. As such it relies on unicast protocols like, e.g, OSPF, RIP, or static routing entries, to figure out the path to all multicast capable neighboring routers. This information is necessary in setups with more than one route between a multicast sender and a receiver to figure out which PIM router should be the active forwarder. However, pimd currently cannot retrieve the unicast routing distance (preference) and metric of routes from the system, not from the kernel nor a route manager like zebra. Hence, pimd currently needs to be setup statically on each router using the desired distance and metric for each active interface. If either the distance and/or the metric is missing in an interface configuration, the following two defaults will be used: default-route-distance <1-255> default: 101 default-route-metric <1-1024> default: 1024 By default pimd starts up on all interfaces it can find, using the above defaults. To configure individual interfaces use: phyint
... You can reference the interface via either its local IPv4 address or its name, e.g., eth0. Some common interface settings are: * `disable`: Disable pimd on this interface, i.e., do not send or listen for PIM-SM traffic * `dr-priority <1-4294967294>`: The DR Priority option, sent in all all PIM Hello messages. Used instead of the IP address in all DR elections, if all PIM routers in LAN advertise it. The higher, the better, default 1. * `distance <1-255>`: The interface's admin distance value (also confusingly referred to as *metric preference* in the RFC) in PIM Assert messages. Used with `metric` to elect the active multicast forwarding router. Defaults to `default-route-distance` * `metric <1-1024>`: The cost for traversing this router. Used with the `preference` value above. Defaults to `default-route-metric` More interface settings are available, see the pimd(8) manual page for the full details. The most notable feature of PIM-SM is that multicast is distributed from so called Rendezvous Points (RP). Each RP handles distribution of one or more multicast groups, pimd can be configured to advertise itself as a candidate RP `rp-candidate`, and request to be static RP `rp-address` for one or more multicast groups. rp-address
[[/ | masklen ] [priority <0-255>] The Rendezvous Point candidate, or CRP, setting is the same as the Cisco `ip pim rp-candidate` setting. Use it to control which interface that should be used in RP elections. * `address | ifname`: Optional local IPv4 address, or interface name to acquire address from. The default is to use the highest active IP address. * `time <10-16383>`: The interval, in seconds, between advertising this CRP. Default: 60 seconds * `priority <0-255>`: How important this CRP is compared to others. The lower the value here, the more important the CRP. Like Cisco, pimd defaults to priority 0 when this is left out In the CRP messages sent out by pimd, one or more multicast groups can be advertised using the following syntax. group-prefix [ | masklen ] Each `group-prefix` setting defines one multicast group and an optional mask length, which defaults to 16 if left out. A maximum of 255 multicast group prefix records is possible for the CRP. To keep track of all Rendezvous Points in a PIM-SM domain there exists a feature called *Bootstrap Router*. The elected BSR in a PIM-SM domain periodically announces the RP set in Bootstrap messages. For details on PIM BSR operation, see [RFC 5059](http://tools.ietf.org/search/rfc5059). bsr-candidate [address | ifname] [priority <0-255>] The configuration of a Candidate BootStrap Router (CBSR) is very similar to that of CRP, except for the interval time. If either the address or the interface name is left out pimd uses the highest active IP address. If the priority is left out, `pimd` (like Cisco) defaults to priority 0. To *disable CRP and CBSR* completely in `pimd`, simply comment the two lines out from your `pimd.conf`, and make sure `pimd` can find the file. Because if `pimd` cannot find the file it will default to them enabled, with defaults listed in the `pimd.conf` included in the distribution. In a PIM-SM domain there can be two, or more, paths from a designated router (DR) for a multicast sender to reach a receiver. When receivers begin joining multicast groups all data is received via the *shared tree* (RPT) from each Rendezvous Point (RP). This is often not an optimal route, so when the volume starts exceeding a configurable threshold, on either the last-hop router or the RP itself, the router will attempt to switch to the *shortest path tree* (SPT) from the multicast source to the receiver. In versions of pimd prior to 2.2.0 this threshold was confusingly split in two different settings, one for the DR and one for the RP. These settings are still supported, for compatibility reasons and documented in the man-page, but it is strongly recommended to change to the new syntax instead: spt-threshold [rate | packets | infinity] [interval <5-60>] Only slightly different from the Cisco `ip pim spt-threshold` setting, pimd can trigger a switch to SPT on a rate or number of packets and you can also tweak the poll interval. It's recommended to keep the interval in the tens of seconds, the default is 100 sec. The default threshold is set to zero packets, which will cause a switch over to the SPT after the first multicast packet is received. Example ------- # Interface eth0 is disabled, i.e., pimd will not run there. phyint eth0 disable # On this LAN we have a lower numeric IP than other PIM routers # but we want to take care of forwarding all PIM messages. phyint eth1 dr-priority 10 # Partake in BSR elections on eth1 bsr-candidate eth1 # Offer to be an RP for all of 224.0.0.0/4 rp-candidate eth1 group-prefix 224.0.0.0 masklen 4 # This is the built-in defaults, switch to SPT on first packet spt-threshold packets 0 interval 100 Starting -------- Having set up the configuration file, you are ready to run pimd. As usual, it is recommended that you start it manually first, to make sure everything works as expected, before adding it to your system's startup scripts, with any startup flags it might need. pimd [-c file] [-d subsys1[,...,subsysN]] [-s level] * `-c file`: Utilize the specified configuration file rather than the default, `/etc/pimd.conf` * `-d [subsys1,...,subsysN]`: Subsystems to enable debug for when running the daemon. Optional argument, if left out, all subsystems are enabled. Type `pimd -h` for a full list of subsystems * `-s level`: Log level, one of `none`, `error`, `warning`, `notice`, `info`, or `debug`. Default is `notice` **Example:** pimd -c /cfg/pimd.conf -digmp_proto,pim_jp,kernel,pim_register Notice the lack of spaces in the option argument to `-d`, the long-option `--debug=igmp_proto,pim_jp,kernel,pim_register`is slightly more readable. Monitoring ---------- To see the virtual interface table, including neighboring PIM routers, and the multicast routing table: pimd -r or to watch it continually: watch pimd -r In addition, pimd logs important events to the system logfile, in particular at startup when parsing the `pimd.conf` configuration file. Contributing ------------ pimd is maintained by [Joachim Nilsson][] at [GitHub][]. If you find bugs, have feature requests, or want to contribute fixes or features, check out the code from GitHub, including the submodules: git clone https://github.com/troglobit/uftpd cd uftpd make submodules When you pull from upstream, remember to also update the submodules using `git submodule update`, see the file [CONTRIBUTING.md][contrib] for further details. [BSD license]: https://en.wikipedia.org/wiki/BSD_licenses [github]: http://github.com/troglobit/pimd [homepage]: http://troglobit.com/pimd.html [changes]: https://github.com/troglobit/pimd/blob/master/ChangeLog.org [the FTP]: http://ftp.troglobit.com/pimd/ [releases page]: https://github.com/troglobit/pimd/releases [contrib]: https://github.com/troglobit/pimd/blob/master/CONTRIBUTING.md [Joachim Nilsson]: http://troglobit.com [Travis]: https://travis-ci.org/troglobit/pimd [Travis Status]: https://travis-ci.org/troglobit/pimd.png?branch=master [Coverity Scan]: https://scan.coverity.com/projects/3319 [Coverity Status]: https://scan.coverity.com/projects/3319/badge.svg pimd-2.3.2/RELEASE.NOTES000066400000000000000000000020221267035112600143400ustar00rootroot00000000000000DEPRECATED ========== See the file ChangeLog for changes over the years. The below is the Release Notes for Pavlin's last pimd release back in 2000, it is kept only as a historical reference and TODO. $Id: RELEASE.NOTES,v 1.3 2000/03/08 09:10:52 pavlin Exp $ * Since pimd cannot reliably get preference and metric info from the kernel a configuration option is provided to specify a preference with which to advertise sources when sending PIM-Assert messages. The phyint command in pimd.conf allows you to specify a default perference for each interface. If no preference is specified, prefs will default to 101, which is high enough that default preferences advertised by either Cisco or Gated routers (both having better information on unicast routes) will will over pimd preferences. Directly connected sources will always be asserted with a preference of 0. * Because unicast routes are obtained from the kernel, only the prefered route is available. * RSRR support is currently rough and completely untested. pimd-2.3.2/TODO.org000066400000000000000000000134021267035112600137300ustar00rootroot00000000000000TODO List -*-org-*- THIS LIST IS FAR AWAY FROM BEING COMPLETE, so these are the few things that came up at the right moment to be written down. * Random issues: ** Run the code checker sparse harder on the code: CC=cgcc make ** Start work on ironing out a TODO list for RFC 4601 compliancy. ** Look into updates RFC 5059 and RFC 5796 ** Test again on Debian GNU/kFreeBSD. Atm it lacks netinet/pim.h, but it should be possible to run the default config on it, probing the __FreeBSD_kernel__ and doing the right thing. ** Check Kame's pim6sd old sources for relevant fixes. http://www.kame.net/dev/cvsweb2.cgi/kame/kame/kame/pim6sd/Attic/ ** Import Kame's pim6sd cfparse.[yl] config file parser reimplemented in lex & yacc, like mrouted does. ** DONE When receive PIM_REGISTER, check whether I am the chosen RP ** Install negative cache in kernel for non-frequently requested groups. ** The code should use the _PIM_VT handling of the 'struct pim' ** Check 2.10 from the spec "Unicast Routing Changes", and verify that it is properly implemented ** in pimd.conf should be mandatory, instead of relaying of its default value (16) ** Candidate RP priority configuration in pimd.conf should be per prefix, instead of a single priority for the whole RP. ** Check whether Asserts received on the iif are really evaluated by using the metrics of other asserts received on that iif, or the comparison uses the local metric and preference info (it must be the former!). ** Experimental kernel MFC (*,G) related: If the (S,G) iif or oifs are different from the (*,G) or (*,*,RP) iifs/oifs, the resp. (*,G) or (*,*,RP) will delete and disallow creating (*,G) MFC. Only after all MRT (S,G) are deleted, the corresponding (*,G) or (*,*,RP) will create (*,G) MFC. ** Experimental kernel MFC (*,G) related: Right now when the MFC (*,G) total datarate is above the SPT switch threshold, the (*,G) MFC will be deleted, and any further cache miss will result in (S,G) MFC (the problem is that we must do (S,G) monitoring for eventually high datagate sources). Only after all (S,G) MFCs expire, the daemon's MRT will stop creating (S,G) MFCs (i.e. the next cache miss will result in (*,G) kernel MFC). A better selection should be applied to sort out the higher datarate sources, and at the same time to have (*,G)MFC as well. For example, create few (S,G), and after that create the (*,G). If some of the created (S,G) MFC entries have very low datarate, delete them. ** Use NetBSD's definition for IPADDR (netinet/in.h): #ifdef _KERNEL #define __IPADDR(x) ((u_int32_t) htonl((u_int32_t)(x))) #else #define __IPADDR(x) ((u_int32_t)(x)) #endif ** The (S,G)RPbit in the DR for the sender and the (S,G)SPT in the downstream router won't timeout and will refresh each other even if the sender is not active: S--DR-----------------R1------------RP (S,G)RPbit (S,G) iif toward S ** Check whether the kernel code sends CACHE_MISS and WRONG_IIF for the LAN-scoped addresses ** If the RP for a group changes, the DR should cancel any PIM-register-stop timers (XXX: not in the spec, but should be there) ** If a new interface is configured, include it automatically ** Don't create routing entries for local link scoped groups ** Implement adm. scoped filters ** Do precise check of the timer events to speed up the propagation of the Cand-RP messages + Cand-BSR messages and the election of the BSR. ** Fix the bug for messing up the things when the receiver is on the same host as the RP for the multicast group (probably was fixed with alpha6, because I cannot reproduce it anymore) ** Do more precise error check for the received PIM messages. In most cases, the whole message must be parsed completely before starting processing it. ** Clean up the debugging messages. ** Use Patricia tree to search the routing table (There is a nice paper in Sigcomm '97 about fast routing tables implementation, so need to check it as well) ** Do switch back to the Shared Tree by timing out the SPT if the rate is too low (not in the spec, but Ahmed pointed out some complications if this happens) ** Change all countdown timers to events timeout (callout.c) (The current implementation is very unefficient if the routing table becomes very large) ** Send immediately Join/Prune, instead of relying of Join/Prune timer = 0 ** Fix the code allowing interface UP/DOWN without restarting pimd. ** Do more testings for SPT switch, Join/Prune, asserts, etc... ** Test the (*,*,RP) code (need PIM/DVMRP border router to do so) ** Test the RSRR (RSVP support) code ** Send Initial_Reply RSRR message if the interfaces detected by pimd change ** SNMP support, RFC2934 * Issues by function name: ** igmp_proto.c: - accept_group_report(): * add a leaf if DR or forwarder (currently only if DR)??? - accept_leave_message(): * send immediately PIM prune message if the last member has left ** main.c - main(): * use a combination of time and hostid to initialize the random generator. - restart(): * check the implementation ** pim_proto.c - pim_register(): * IF THE BORDER BIT IS SET, THEN FORWARD THE WHOLE PACKET FROM USER SPACE AND AT THE SAME TIME IGNORE ANY CACHE_MISS SIGNALS FROM THE KERNEL. - register_stop(): * REGISTER_STOP rate limiting ** route.c - process_cache_miss() * use negative cache. ** rp.c - add_rp_grp_entry(): * FIX THE BUG when adding an RP for different prefix requires remapping for some groups!!! (Intentionally left, waiting to come up with an idea how to implement it simple and efficient. If you configure all RPs to advertise the same prefix, the bug won't "show up") pimd-2.3.2/callout.c000066400000000000000000000116161267035112600142660ustar00rootroot00000000000000/* * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". Use of the mrouted program represents acceptance * of the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * callout.c,v 3.8.4.5 1997/05/16 20:18:25 fenner Exp */ #include "defs.h" /* the code below implements a callout queue */ static int id = 0; static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */ struct timeout_q { struct timeout_q *next; /* next event */ int id; cfunc_t func; /* function to call */ void *data; /* func's data */ int time; /* time offset to next event*/ }; #if 0 #define CALLOUT_DEBUG 1 #define CALLOUT_DEBUG2 1 #endif /* 0 */ #ifdef CALLOUT_DEBUG2 static void print_Q((void); #else #define print_Q() #endif void callout_init() { Q = (struct timeout_q *) 0; } void free_all_callouts() { struct timeout_q *p; while (Q) { p = Q; Q = Q->next; free(p); } } /* * elapsed_time seconds have passed; perform all the events that should * happen. */ void age_callout_queue(elapsed_time) int elapsed_time; { struct timeout_q *ptr, *expQ; #ifdef CALLOUT_DEBUG IF_DEBUG(DEBUG_TIMEOUT) logit(LOG_DEBUG, 0, "aging queue (elapsed time %d):", elapsed_time); print_Q(); #endif expQ = Q; ptr = NULL; while (Q) { if (Q->time > elapsed_time) { Q->time -= elapsed_time; if (ptr) { ptr->next = NULL; break; } return; } else { elapsed_time -= Q->time; ptr = Q; Q = Q->next; } } /* handle queue of expired timers */ while (expQ) { ptr = expQ; if (ptr->func) ptr->func(ptr->data); expQ = expQ->next; free(ptr); } } /* * Return in how many seconds age_callout_queue() would like to be called. * Return -1 if there are no events pending. */ int timer_nextTimer() { if (Q) { if (Q->time < 0) { logit(LOG_WARNING, 0, "timer_nextTimer top of queue says %d", Q->time); return 0; } return Q->time; } return -1; } /* * sets the timer */ int timer_setTimer(delay, action, data) int delay; /* number of units for timeout */ cfunc_t action; /* function to be called on timeout */ void *data; /* what to call the timeout function with */ { struct timeout_q *ptr, *node, *prev; #ifdef CALLOUT_DEBUG IF_DEBUG(DEBUG_TIMEOUT) logit(LOG_DEBUG, 0, "setting timer:"); print_Q(); #endif /* create a node */ node = (struct timeout_q *)calloc(1, sizeof(struct timeout_q)); if (!node) { logit(LOG_ERR, 0, "Failed calloc() in timer_settimer\n"); return -1; } node->func = action; node->data = data; node->time = delay; node->next = 0; node->id = ++id; prev = ptr = Q; /* insert node in the queue */ /* if the queue is empty, insert the node and return */ if (!Q) Q = node; else { /* chase the pointer looking for the right place */ while (ptr) { if (delay < ptr->time) { /* right place */ node->next = ptr; if (ptr == Q) Q = node; else prev->next = node; ptr->time -= node->time; print_Q(); return node->id; } else { /* keep moving */ delay -= ptr->time; node->time = delay; prev = ptr; ptr = ptr->next; } } prev->next = node; } print_Q(); return node->id; } /* returns the time until the timer is scheduled */ int timer_leftTimer(timer_id) int timer_id; { struct timeout_q *ptr; int left = 0; if (!timer_id) return -1; for (ptr = Q; ptr; ptr = ptr->next) { left += ptr->time; if (ptr->id == timer_id) return left; } return -1; } /* clears the associated timer */ void timer_clearTimer(timer_id) int timer_id; { struct timeout_q *ptr, *prev; if (!timer_id) return; prev = ptr = Q; /* * find the right node, delete it. the subsequent node's time * gets bumped up */ print_Q(); while (ptr) { if (ptr->id == timer_id) { /* got the right node */ /* unlink it from the queue */ if (ptr == Q) Q = Q->next; else prev->next = ptr->next; /* increment next node if any */ if (ptr->next != 0) (ptr->next)->time += ptr->time; if (ptr->data) free(ptr->data); free(ptr); print_Q(); return; } prev = ptr; ptr = ptr->next; } print_Q(); } #ifdef CALLOUT_DEBUG2 /* * debugging utility */ static void print_Q() { struct timeout_q *ptr; IF_DEBUG(DEBUG_TIMEOUT) for (ptr = Q; ptr; ptr = ptr->next) logit(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time); } #endif /* CALLOUT_DEBUG2 */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/config.c000066400000000000000000001345271267035112600140770ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #define WARN(fmt, args...) logit(LOG_WARNING, 0, "%s:%u - " fmt, config_file, lineno, ##args) #define BAILOUT(msg, arg...) { WARN(msg ", bailing out!", ##arg); return FALSE; } #define IGNORING(msg, arg...) { WARN(msg ", ignoring ...", ##arg); continue; } /* Helper macros */ #define QUERIER_TIMEOUT(qintv) (IGMP_ROBUSTNESS_VARIABLE * (qintv) + IGMP_QUERY_RESPONSE_INTERVAL / 2) #define LINE_BUFSIZ 1024 /* Max. line length of the config file */ #define CONF_UNKNOWN -1 #define CONF_EMPTY 1 #define CONF_PHYINT 2 #define CONF_CANDIDATE_RP 3 #define CONF_RP_ADDRESS 4 #define CONF_GROUP_PREFIX 5 #define CONF_BOOTSTRAP_RP 6 #define CONF_COMPAT_THRESHOLD 7 #define CONF_SPT_THRESHOLD 8 #define CONF_DEFAULT_ROUTE_METRIC 9 #define CONF_DEFAULT_ROUTE_DISTANCE 10 #define CONF_ALTNET 11 #define CONF_MASKLEN 12 #define CONF_SCOPED 13 #define CONF_IGMP_QUERY_INTERVAL 14 #define CONF_IGMP_QUERIER_TIMEOUT 15 #define CONF_HELLO_INTERVAL 16 /* * Global settings */ uint16_t pim_timer_hello_interval = PIM_TIMER_HELLO_INTERVAL; uint16_t pim_timer_hello_holdtime = PIM_TIMER_HELLO_HOLDTIME; /* * Forward declarations. */ static char *next_word (char **); static int parse_phyint (char *s); static uint32_t ifname2addr (char *s); static uint32_t lineno; extern struct rp_hold *g_rp_hold; /* * Query the kernel to find network interfaces that are multicast-capable * and install them in the uvifs array. */ void config_vifs_from_kernel(void) { struct ifreq *ifrp, *ifend; struct uvif *v; vifi_t vifi; uint32_t n; uint32_t addr, mask, subnet; short flags; int num_ifreq = 64; struct ifconf ifc; char *newbuf; total_interfaces = 0; /* The total number of physical interfaces */ ifc.ifc_len = num_ifreq * sizeof(struct ifreq); ifc.ifc_buf = calloc(ifc.ifc_len, sizeof(char)); while (ifc.ifc_buf) { if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0) logit(LOG_ERR, errno, "Failed querying kernel network interfaces"); /* * If the buffer was large enough to hold all the addresses * then break out, otherwise increase the buffer size and * try again. * * The only way to know that we definitely had enough space * is to know that there was enough space for at least one * more struct ifreq. ??? */ if ((num_ifreq * sizeof(struct ifreq)) >= ifc.ifc_len + sizeof(struct ifreq)) break; num_ifreq *= 2; ifc.ifc_len = num_ifreq * sizeof(struct ifreq); newbuf = realloc(ifc.ifc_buf, ifc.ifc_len); if (newbuf == NULL) free(ifc.ifc_buf); ifc.ifc_buf = newbuf; } if (ifc.ifc_buf == NULL) logit(LOG_ERR, 0, "config_vifs_from_kernel() ran out of memory"); ifrp = (struct ifreq *)ifc.ifc_buf; ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); /* * Loop through all of the interfaces. */ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) { struct ifreq ifr; memset (&ifr, 0, sizeof (ifr)); #ifdef HAVE_SA_LEN n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (n < sizeof(*ifrp)) n = sizeof(*ifrp); #else n = sizeof(*ifrp); #endif /* HAVE_SA_LEN */ /* * Ignore any interface for an address family other than IP. */ if (ifrp->ifr_addr.sa_family != AF_INET) { total_interfaces++; /* Eventually may have IP address later */ continue; } addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr; /* * Need a template to preserve address info that is * used below to locate the next entry. (Otherwise, * SIOCGIFFLAGS stomps over it because the requests * are returned in a union.) */ memcpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); /* * Ignore loopback interfaces and interfaces that do not * support multicast. */ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) logit(LOG_ERR, errno, "Failed reading interface flags for phyint %s", ifr.ifr_name); flags = ifr.ifr_flags; if ((flags & (IFF_LOOPBACK | IFF_MULTICAST)) != IFF_MULTICAST) continue; /* * Everyone below is a potential vif interface. * We don't care if it has wrong configuration or not configured * at all. */ total_interfaces++; /* * Ignore any interface whose address and mask do not define a * valid subnet number, or whose address is of the form * {subnet,0} or {subnet,-1}. */ if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0) { if (!(flags & IFF_POINTOPOINT)) logit(LOG_ERR, errno, "Failed reading interface netmask for phyint %s", ifr.ifr_name); mask = 0xffffffff; } else { mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; } subnet = addr & mask; #ifdef DISABLE_MASKLEN_CHECK if (mask != 0xffffffff) { #endif if ((!inet_valid_subnet(subnet, mask)) || (addr == subnet) || addr == (subnet | ~mask)) { if (!(inet_valid_host(addr) && ((mask == htonl(0xfffffffe)) || (flags & IFF_POINTOPOINT)))) { logit(LOG_WARNING, 0, "Ignoring %s, has invalid address %s and/or netmask %s", ifr.ifr_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(mask, s2, sizeof(s2))); continue; } } #ifdef DISABLE_MASKLEN_CHECK } #endif /* * Ignore any interface that is connected to the same subnet as * one already installed in the uvifs array. */ /* * TODO: XXX: bug or "feature" is to allow only one interface per * subnet? */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (strcmp(v->uv_name, ifr.ifr_name) == 0) { logit(LOG_DEBUG, 0, "Ignoring %s (%s on subnet %s) (alias for vif#%u?)", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), vifi); break; } /* we don't care about point-to-point links in same subnet */ if (flags & IFF_POINTOPOINT) continue; if (v->uv_flags & VIFF_POINT_TO_POINT) continue; #if 0 /* * TODO: to allow different interfaces belong to * overlapping subnet addresses, use this version instead */ if (((addr & mask ) == v->uv_subnet) && (v->uv_subnetmask == mask)) { logit(LOG_WARNING, 0, "Ignoring %s, same subnet as %s", ifr.ifr_name, v->uv_name); break; } #else if ((addr & v->uv_subnetmask) == v->uv_subnet || (v->uv_subnet & mask) == subnet) { logit(LOG_WARNING, 0, "Ignoring %s, same subnet as %s", ifr.ifr_name, v->uv_name); break; } #endif /* 0 */ } if (vifi != numvifs) continue; /* * If there is room in the uvifs array, install this interface. */ if (numvifs == MAXVIFS) { logit(LOG_WARNING, 0, "Too many vifs, ignoring %s", ifr.ifr_name); continue; } v = &uvifs[numvifs]; zero_vif(v, FALSE); v->uv_lcl_addr = addr; v->uv_subnet = subnet; v->uv_subnetmask = mask; if (mask != htonl(0xfffffffe)) v->uv_subnetbcast = subnet | ~mask; else v->uv_subnetbcast = 0xffffffff; strlcpy(v->uv_name, ifr.ifr_name, IFNAMSIZ); /* * Figure out MTU of interface, needed as a seed value when * fragmenting PIM register messages. We should really do * a PMTU check on initial PIM register send to a new RP... */ if (ioctl(udp_socket, SIOCGIFMTU, &ifr) < 0) v->uv_mtu = 1500; else v->uv_mtu = ifr.ifr_mtu; if (flags & IFF_POINTOPOINT) { v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT); if (ioctl(udp_socket, SIOCGIFDSTADDR, (char *)&ifr) < 0) logit(LOG_ERR, errno, "Failed reading point-to-point address for %s", v->uv_name); else v->uv_rmt_addr = ((struct sockaddr_in *)(&ifr.ifr_dstaddr))->sin_addr.s_addr; } else if (mask == htonl(0xfffffffe)) { /* * Handle RFC 3021 /31 netmasks as point-to-point links */ v->uv_flags |= (VIFF_REXMIT_PRUNES | VIFF_POINT_TO_POINT); if (addr == subnet) v->uv_rmt_addr = addr + htonl(1); else v->uv_rmt_addr = subnet; } #ifdef __linux__ { struct ifreq ifridx; memset(&ifridx, 0, sizeof(ifridx)); strlcpy(ifridx.ifr_name,v->uv_name, IFNAMSIZ); if (ioctl(udp_socket, SIOGIFINDEX, (char *) &ifridx) < 0) logit(LOG_ERR, errno, "Failed reading interface index for %s", ifridx.ifr_name); v->uv_ifindex = ifridx.ifr_ifindex; } if (v->uv_flags & VIFF_POINT_TO_POINT) { logit(LOG_INFO, 0, "Installing %s (%s -> %s) as vif #%u-%d - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(v->uv_rmt_addr, s2, sizeof(s2)), numvifs, v->uv_ifindex, v->uv_rate_limit); } else { logit(LOG_INFO, 0, "Installing %s (%s on subnet %s) as vif #%u-%d - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), numvifs, v->uv_ifindex, v->uv_rate_limit); } #else /* !__linux__ */ if (v->uv_flags & VIFF_POINT_TO_POINT) { logit(LOG_INFO, 0, "Installing %s (%s -> %s) as vif #%u - rate=%d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), inet_fmt(v->uv_rmt_addr, s2, sizeof(s2)), numvifs, v->uv_rate_limit); } else { logit(LOG_INFO, 0, "Installing %s (%s on subnet %s) as vif #%u - rate %d", v->uv_name, inet_fmt(addr, s1, sizeof(s1)), netname(subnet, mask), numvifs, v->uv_rate_limit); } #endif /* __linux__ */ ++numvifs; /* * If the interface is not yet up, set the vifs_down flag to * remind us to check again later. */ if (!(flags & IFF_UP)) { v->uv_flags |= VIFF_DOWN; vifs_down = TRUE; } } } static int deprecated(char *word, char *new_word, int code) { WARN("The %s option is deprecated, replaced with %s", word, new_word); WARN("Please update your configuration file!"); return code; } /** * parse_option - Convert result of string comparisons into numerics. * @input: Pointer to the word * * This function is called by config_vifs_from_file(). * * Returns: * A number corresponding to the code of the word, or %CONF_UNKNOWN. */ static int parse_option(char *word) { if (EQUAL(word, "")) return CONF_EMPTY; if (EQUAL(word, "phyint")) return CONF_PHYINT; if (EQUAL(word, "bsr-candidate")) return CONF_BOOTSTRAP_RP; if (EQUAL(word, "rp-candidate")) return CONF_CANDIDATE_RP; if (EQUAL(word, "rp-address")) return CONF_RP_ADDRESS; if (EQUAL(word, "group-prefix")) return CONF_GROUP_PREFIX; if (EQUAL(word, "spt-threshold")) return CONF_SPT_THRESHOLD; if (EQUAL(word, "default-route-metric")) return CONF_DEFAULT_ROUTE_METRIC; if (EQUAL(word, "default-route-distance")) return CONF_DEFAULT_ROUTE_DISTANCE; if (EQUAL(word, "igmp-query-interval")) return CONF_IGMP_QUERY_INTERVAL; if (EQUAL(word, "igmp-querier-timeout")) return CONF_IGMP_QUERIER_TIMEOUT; if (EQUAL(word, "altnet")) return CONF_ALTNET; if (EQUAL(word, "masklen")) return CONF_MASKLEN; if (EQUAL(word, "scoped")) return CONF_SCOPED; if (EQUAL(word, "hello-interval")) return CONF_HELLO_INTERVAL; /* Compatibility with old config files that use _ instead of - */ if (EQUAL(word, "cand_bootstrap_router")) return CONF_BOOTSTRAP_RP; if (EQUAL(word, "cand_rp")) return CONF_CANDIDATE_RP; if (EQUAL(word, "group_prefix")) return CONF_GROUP_PREFIX; if (EQUAL(word, "rp_address")) return CONF_RP_ADDRESS; if (EQUAL(word, "switch_register_threshold")) return deprecated(word, "spt-threshold", CONF_COMPAT_THRESHOLD); if (EQUAL(word, "switch_data_threshold")) return deprecated(word, "spt-threshold", CONF_COMPAT_THRESHOLD); if (EQUAL(word, "spt_threshold")) return CONF_SPT_THRESHOLD; if (EQUAL(word, "default_source_metric")) return CONF_DEFAULT_ROUTE_METRIC; if (EQUAL(word, "default_source_preference")) return CONF_DEFAULT_ROUTE_DISTANCE; if (EQUAL(word, "default_igmp_query_interval")) /* compat */ return CONF_IGMP_QUERY_INTERVAL; if (EQUAL(word, "default_igmp_querier_timeout")) /* compat */ return CONF_IGMP_QUERIER_TIMEOUT; if (EQUAL(word, "hello_period")) return CONF_HELLO_INTERVAL; return CONF_UNKNOWN; } /* Check for optional /PREFIXLEN suffix to the address/group */ static void parse_prefix_len(char *token, uint32_t *len) { char *masklen = strchr(token, '/'); if (masklen) { *masklen = 0; masklen++; if (!sscanf(masklen, "%u", len)) { WARN("Invalid masklen '%s'", masklen); *len = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } } } static void validate_prefix_len(uint32_t *len) { if (*len > (sizeof(uint32_t) * 8)) { *len = (sizeof(uint32_t) * 8); } else if (*len < PIM_GROUP_PREFIX_MIN_MASKLEN) { WARN("Too small masklen %u. Defaulting to %d", *len, PIM_GROUP_PREFIX_MIN_MASKLEN); *len = PIM_GROUP_PREFIX_MIN_MASKLEN; } } /** * parse_phyint - Parse physical interface configuration, if any. * @s: String token * * Syntax: * phyint [disable | enable] * [igmpv2 | igmpv3] * [dr-priority <1-4294967294>] * [ttl-threshold <1-255>] * [distance <1-255>] [metric <1-1024>] * [altnet /] * [altnet masklen ] * [scoped /] * [scoped masklen ] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ static int parse_phyint(char *s) { char *w, c; uint32_t local, altnet_addr, scoped_addr; vifi_t vifi; struct uvif *v; uint32_t n, altnet_masklen = 0, scoped_masklen = 0; struct phaddr *ph; struct vif_acl *v_acl; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing phyint address"); return FALSE; } local = ifname2addr(w); if (!local) { local = inet_parse(w, 4); if (!inet_valid_host(local)) { WARN("Invalid phyint address '%s'", w); return FALSE; } } for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (vifi == numvifs) { WARN("phyint %s is not a valid interface", inet_fmt(local, s1, sizeof(s1))); return FALSE; } if (local != v->uv_lcl_addr) continue; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "disable")) { v->uv_flags |= VIFF_DISABLED; continue; } if (EQUAL(w, "enable")) { v->uv_flags &= ~VIFF_DISABLED; continue; } if (EQUAL(w, "igmpv2")) { v->uv_flags &= ~VIFF_IGMPV1; v->uv_flags |= VIFF_IGMPV2; continue; } if (EQUAL(w, "igmpv3")) { v->uv_flags &= ~VIFF_IGMPV1; v->uv_flags &= ~VIFF_IGMPV2; continue; } if (EQUAL(w, "altnet")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing ALTNET for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } parse_prefix_len (w, &altnet_masklen); altnet_addr = ifname2addr(w); if (!altnet_addr) { altnet_addr = inet_parse(w, 4); if (!inet_valid_host(altnet_addr)) { WARN("Invalid altnet address '%s'", w); return FALSE; } } if (EQUAL((w = next_word(&s)), "masklen")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing ALTNET masklen for phyint %s", inet_fmt(local, s1, sizeof (s1))); continue; } if (!sscanf(w, "%u", &altnet_masklen)) { WARN("Invalid altnet masklen '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } } ph = (struct phaddr *)calloc(1, sizeof(struct phaddr)); if (!ph) return FALSE; if (altnet_masklen) { VAL_TO_MASK(ph->pa_subnetmask, altnet_masklen); } else { ph->pa_subnetmask = v->uv_subnetmask; } ph->pa_subnet = altnet_addr & ph->pa_subnetmask; ph->pa_subnetbcast = ph->pa_subnet | ~ph->pa_subnetmask; if (altnet_addr & ~ph->pa_subnetmask) WARN("Extra subnet %s/%d has host bits set", inet_fmt(altnet_addr, s1, sizeof(s1)), altnet_masklen); ph->pa_next = v->uv_addrs; v->uv_addrs = ph; logit(LOG_DEBUG, 0, "ALTNET: %s/%d", inet_fmt(altnet_addr, s1, sizeof(s1)), altnet_masklen); } /* altnet */ /* scoped mcast groups/masklen */ if (EQUAL(w, "scoped")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing SCOPED for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } parse_prefix_len (w, &scoped_masklen); scoped_addr = ifname2addr(w); if (!scoped_addr) { scoped_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(scoped_addr))) { WARN("Invalid scoped address '%s'", w); return FALSE; } } if (EQUAL((w = next_word(&s)), "masklen")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing SCOPED masklen for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u", &scoped_masklen) != 1) { WARN("Invalid scoped masklen '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } } /* Invalid config. VAL_TO_MASK() also requires len > 0 or shift op will fail. */ if (!scoped_masklen) { WARN("Too small (0) scoped masklen for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } v_acl = (struct vif_acl *)calloc(1, sizeof(struct vif_acl)); if (!v_acl) return FALSE; VAL_TO_MASK(v_acl->acl_mask, scoped_masklen); v_acl->acl_addr = scoped_addr & v_acl->acl_mask; if (scoped_addr & ~v_acl->acl_mask) WARN("Boundary spec %s/%d has host bits set", inet_fmt(scoped_addr, s1, sizeof(s1)), scoped_masklen); v_acl->acl_next = v->uv_acl; v->uv_acl = v_acl; logit(LOG_DEBUG, 0, "SCOPED %s/%x", inet_fmt(v_acl->acl_addr, s1, sizeof(s1)), v_acl->acl_mask); } /* scoped */ if (EQUAL(w, "ttl-threshold") || EQUAL(w, "threshold")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing threshold for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 255 ) { WARN("Invalid threshold '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } v->uv_threshold = n; continue; } /* threshold */ if (EQUAL(w, "distance") || EQUAL(w, "preference")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing distance value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 255 ) { WARN("Invalid distance value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_ASSERT) logit(LOG_DEBUG, 0, "Config setting default local preference on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_local_pref = n; continue; } if (EQUAL(w, "metric")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing metric value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 1024 ) { WARN("Invalid metric value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_ASSERT) logit(LOG_DEBUG, 0, "Setting default local metric on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_local_metric = n; continue; } if (EQUAL(w, "dr-priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing dr-priority value for phyint %s", inet_fmt(local, s1, sizeof(s1))); continue; } if (sscanf(w, "%u%c", &n, &c) != 1 || n < 1 || n > 4294967294u) { WARN("Invalid dr-priority value '%s' for phyint %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } IF_DEBUG(DEBUG_PIM_HELLO) logit(LOG_DEBUG, 0, "Setting dr-priority on %s to %d", inet_fmt(local, s1, sizeof(s1)), n); v->uv_dr_prio = n; continue; } } /* while(... != "") */ break; } return TRUE; } /** * parse_rp_candidate - Parse candidate Rendez-Vous Point information. * @s: String token * * Syntax: * rp-candidate [address | ifname] [priority <0-255>] [time <10-16383>] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ int parse_rp_candidate(char *s) { u_int time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; u_int priority = PIM_DEFAULT_CAND_RP_PRIORITY; char *w; uint32_t local = INADDR_ANY_N; cand_rp_flag = FALSE; my_cand_rp_adv_period = PIM_DEFAULT_CAND_RP_ADV_PERIOD; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing priority, defaulting to %u", w, PIM_DEFAULT_CAND_RP_PRIORITY); priority = PIM_DEFAULT_CAND_RP_PRIORITY; continue; } if (sscanf(w, "%u", &priority) != 1) { WARN("Invalid priority %s, defaulting to %u", w, PIM_DEFAULT_CAND_RP_PRIORITY); priority = PIM_DEFAULT_CAND_RP_PRIORITY; } if (priority > PIM_MAX_CAND_RP_PRIORITY) { WARN("Too high Cand-RP priority %u, defaulting to %d", priority, PIM_MAX_CAND_RP_PRIORITY); priority = PIM_MAX_CAND_RP_PRIORITY; } continue; } if (EQUAL(w, "time")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing Cand-RP announce interval, defaulting to %u", PIM_DEFAULT_CAND_RP_ADV_PERIOD); time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; continue; } if (sscanf(w, "%u", &time) != 1) { WARN("Invalid Cand-RP announce interval, defaulting to %u", PIM_DEFAULT_CAND_RP_ADV_PERIOD); time = PIM_DEFAULT_CAND_RP_ADV_PERIOD; continue; } if (time < PIM_MIN_CAND_RP_ADV_PERIOD) time = PIM_MIN_CAND_RP_ADV_PERIOD; if (time > PIM_MAX_CAND_RP_ADV_PERIOD) time = PIM_MAX_CAND_RP_ADV_PERIOD; my_cand_rp_adv_period = time; continue; } /* Cand-RP interface or address */ local = ifname2addr(w); if (!local) local = inet_parse(w, 4); if (!inet_valid_host(local)) { local = max_local_address(); WARN("Invalid Cand-RP address '%s', defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } else if (local_address(local) == NO_VIF) { local = max_local_address(); WARN("Cand-RP address '%s' is not local, defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } } if (local == INADDR_ANY_N) { /* If address not provided, use the max. local */ local = max_local_address(); } my_cand_rp_address = local; my_cand_rp_priority = priority; my_cand_rp_adv_period = time; cand_rp_flag = TRUE; logit(LOG_INFO, 0, "Local Cand-RP address %s, priority %u, interval %u sec", inet_fmt(local, s1, sizeof(s1)), priority, time); return TRUE; } /** * parse_group_prefix - Parse group-prefix configured information. * @s: String token * Syntax: * group-prefix [/] * [masklen ] * * Returns: * %TRUE if the parsing was successful, o.w. %FALSE */ int parse_group_prefix(char *s) { char *w; uint32_t group_addr; uint32_t masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; w = next_word(&s); if (EQUAL(w, "")) { WARN("Missing group-prefix address"); return FALSE; } parse_prefix_len (w, &masklen); group_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(group_addr))) { WARN("Group address '%s' is not a valid multicast address", inet_fmt(group_addr, s1, sizeof(s1))); return FALSE; } /* Was if (!(~(*cand_rp_adv_message.prefix_cnt_ptr))) which Arm GCC 4.4.2 dislikes: * --> "config.c:693: warning: promoted ~unsigned is always non-zero" * The prefix_cnt_ptr is a uint8_t so it seems this check was to prevent overruns. * I've changed the check to see if we've already read 255 entries, if so the cnt * is maximized and we need to tell the user. --Joachim Nilsson 2010-01-16 */ if (*cand_rp_adv_message.prefix_cnt_ptr == 255) { WARN("Too many multicast groups configured!"); return FALSE; } if (EQUAL((w = next_word(&s)), "masklen")) { w = next_word(&s); if (!sscanf(w, "%u", &masklen)) masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } validate_prefix_len(&masklen); PUT_EGADDR(group_addr, (uint8_t)masklen, 0, cand_rp_adv_message.insert_data_ptr); (*cand_rp_adv_message.prefix_cnt_ptr)++; logit(LOG_INFO, 0, "Adding Cand-RP group prefix %s/%d", inet_fmt(group_addr, s1, sizeof(s1)), masklen); return TRUE; } /** * parse_bsr_candidate - Parse the candidate BSR configured information. * @s: String token * * Syntax: * bsr-candidate [address | ifname] [priority <0-255>] */ int parse_bsr_candidate(char *s) { char *w; uint32_t local = INADDR_ANY_N; uint32_t priority = PIM_DEFAULT_BSR_PRIORITY; cand_bsr_flag = FALSE; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "priority")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing Cand-BSR priority, defaulting to %u", PIM_DEFAULT_BSR_PRIORITY); priority = PIM_DEFAULT_BSR_PRIORITY; continue; } if (sscanf(w, "%u", &priority) != 1) { WARN("Invalid Cand-BSR priority %s, defaulting to %u", PIM_DEFAULT_BSR_PRIORITY); priority = PIM_DEFAULT_BSR_PRIORITY; continue; } if (priority > PIM_MAX_CAND_BSR_PRIORITY) { WARN("Too high Cand-BSR priority %u, defaulting to %d", priority, PIM_MAX_CAND_BSR_PRIORITY); priority = PIM_MAX_CAND_BSR_PRIORITY; } my_bsr_priority = (uint8_t)priority; continue; } /* Cand-BSR interface or address */ local = ifname2addr(w); if (!local) local = inet_parse(w, 4); if (!inet_valid_host(local)) { local = max_local_address(); WARN("Invalid Cand-BSR address '%s', defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); continue; } if (local_address(local) == NO_VIF) { local = max_local_address(); WARN("Cand-BSR address '%s' is not local, defaulting to %s", w, inet_fmt(local, s1, sizeof(s1))); } } if (local == INADDR_ANY_N) { /* If address not provided, use the max. local */ local = max_local_address(); } my_bsr_address = local; my_bsr_priority = priority; MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, my_bsr_hash_mask); cand_bsr_flag = TRUE; logit(LOG_INFO, 0, "Local Cand-BSR address %s, priority %u", inet_fmt(local, s1, sizeof(s1)), priority); return TRUE; } /** * parse_rp_address - Parse rp-address config option. * @s: String token. * * This is an extension to the original pimd to add pimd.conf support for static * Rendez-Vous Point addresses. * * The function has been extended by pjf@asn.pl, of Lintrack, to allow specifying * multicast group addresses as well. * * Syntax: * rp-address
[[ masklen ] * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_rp_address(char *s) { char *w; uint32_t local = 0xffffff; uint32_t group_addr = htonl(INADDR_UNSPEC_GROUP); uint32_t masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; struct rp_hold *rph; /* next is RP addr */ w = next_word(&s); if (EQUAL(w, "")) { logit(LOG_WARNING, 0, "Missing rp-address argument"); return FALSE; } local = inet_parse(w, 4); if (local == 0xffffff) { WARN("Invalid rp-address %s", w); return FALSE; } /* next is group addr if exist */ w = next_word(&s); if (!EQUAL(w, "")) { parse_prefix_len (w, &masklen); group_addr = inet_parse(w, 4); if (!IN_MULTICAST(ntohl(group_addr))) { WARN("%s is not a valid multicast address", inet_fmt(group_addr, s1, sizeof(s1))); return FALSE; } /* next is prefix or priority if exist */ while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "masklen")) { w = next_word(&s); if (!sscanf(w, "%u", &masklen)) { WARN("Invalid masklen %s. Defaulting to %d)", w, PIM_GROUP_PREFIX_DEFAULT_MASKLEN); masklen = PIM_GROUP_PREFIX_DEFAULT_MASKLEN; } } /* Unused, but keeping for backwards compatibility for people who * may still have this option in their pimd.conf * The priority of a static RP is hardcoded to always be 1, see Juniper's * configuration or similar sources for reference. */ if (EQUAL(w, "priority")) { w = next_word(&s); WARN("The priority of static RP's is, as of pimd 2.2.0, always 1."); } } } else { group_addr = htonl(INADDR_UNSPEC_GROUP); masklen = PIM_GROUP_PREFIX_MIN_MASKLEN; } validate_prefix_len(&masklen); rph = calloc(1, sizeof(*rph)); if (!rph) { logit(LOG_WARNING, 0, "Out of memory when parsing rp-address %s", inet_fmt(local, s1, sizeof(s1))); return FALSE; } rph->address = local; rph->group = group_addr; VAL_TO_MASK(rph->mask, masklen); /* attach at the beginning */ rph->next = g_rp_hold; g_rp_hold = rph; logit(LOG_INFO, 0, "Local static RP: %s, group %s/%d", inet_fmt(local, s1, sizeof(s1)), inet_fmt(group_addr, s2, sizeof(s2)), masklen); return TRUE; } /** * parse_compat_threshold - Parse old deprecated pimd.conf thresholds * @line: * * This is a backwards compatible parser for the two older threshold * settings used in pimd prior to v2.2.0. The switchover mechanism has * been completely changed, however, so we simply read the settings as * if they where the same as the new spt-threshold, only converting the * rate argument differently (bps vs kbps). Last line to be read is * what is activated in pimd as spt-threshold. * * Note, previously the parser was very lenient to errors, but since the * default has changed it is much more strict. Any syntax error and pimd * bails out ignoring the line. * * Syntax: * switch_register_threshold [rate interval ] * switch_data_threshold [rate interval ] * * Returns: * When parsing @line is successful, returns %TRUE, otherwise %FALSE. */ static int parse_compat_threshold(char *line) { char *w; int rate = -1; int interval = -1; while (!EQUAL((w = next_word(&line)), "")) { if (EQUAL(w, "rate")) { if (EQUAL((w = next_word(&line)), "")) BAILOUT("Missing rate value in compat threshold parser"); /* 10 --> 1,000,000,000 == 100 Gbps */ if (sscanf(w, "%10d", &rate) != 1) BAILOUT("Invalid rate value %s in compat threshold parser", w); continue; } if (EQUAL(w, "interval")) { if (EQUAL((w = next_word(&line)), "")) IGNORING("Missing interval value in compat threshold parser"); /* 5 --> 99,999 ~= 27h */ if (sscanf(w, "%5d", &interval) != 1) IGNORING("Invalid interval %s in compat threshold parser", w); continue; } } /* Set polling mode */ spt_threshold.mode = SPT_RATE; /* Only accept values if they don't messup for new spt-threshold */ if (interval >= TIMER_INTERVAL) spt_threshold.interval = interval; /* Accounting for headers we can approximate 1 byte/s == 10 bits/s (bps) */ spt_threshold.bytes = rate * spt_threshold.interval / 10; logit(LOG_INFO, 0, "Compatibility set spt-treshold rate %u kbps with interval %u sec", spt_threshold.bytes, spt_threshold.interval); return TRUE; } /** * parse_hello_interval - Parse and assign the hello interval * @s: Input data * * Syntax: * hello-interval * * Returns: * %TRUE if successful, otherwise %FALSE. */ int parse_hello_interval(char *s) { char *w; u_int period; u_int holdtime; if (!EQUAL((w = next_word(&s)), "")) { if (sscanf(w, "%u", &period) != 1) { logit(LOG_WARNING, 0, "Invalid hello-interval %s; defaulting to %u", w, PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } else { if (period <= (u_int)(UINT16_MAX / 3.5)) { holdtime = period * 3.5; } else { logit(LOG_WARNING, 0, "Too large hello-interval %s; defaulting to %u", w, PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } } } else { logit(LOG_WARNING, 0, "Missing hello-interval value; defaulting to %u", PIM_TIMER_HELLO_INTERVAL); period = PIM_TIMER_HELLO_INTERVAL; holdtime = PIM_TIMER_HELLO_HOLDTIME; } logit(LOG_INFO, 0, "hello-interval is %u", period); pim_timer_hello_interval = period; pim_timer_hello_holdtime = holdtime; return TRUE; } /** * parse_spt_threshold - Parse spt-threshold option * @s: String token * * This configuration setting replaces the switch_register_threshold and * switch_data_threshold. It is more intuitive and more in line with * what major vendors are also using. * * Note that the rate is in kbps instead of bps, compared to the old * syntax. Both the above parse_compat_threshold() and this function * target the same backend. * * Syntax: * spt-threshold [rate | packets | infinity] [interval ] * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_spt_threshold(char *s) { char *w; uint32_t rate = SPT_THRESHOLD_DEFAULT_RATE; uint32_t packets = SPT_THRESHOLD_DEFAULT_PACKETS; uint32_t interval = SPT_THRESHOLD_DEFAULT_INTERVAL; spt_mode_t mode = SPT_THRESHOLD_DEFAULT_MODE; while (!EQUAL((w = next_word(&s)), "")) { if (EQUAL(w, "rate")) { mode = SPT_RATE; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold rate argument, defaulting to %u", SPT_THRESHOLD_DEFAULT_RATE); rate = SPT_THRESHOLD_DEFAULT_RATE; continue; } /* 10 --> 1,000,000,000 == 100 Gbps */ if (sscanf(w, "%10u", &rate) != 1) { WARN("Invalid spt-threshold rate %s, defaulting to %u", w, SPT_THRESHOLD_DEFAULT_RATE); rate = SPT_THRESHOLD_DEFAULT_RATE; } continue; } if (EQUAL(w, "interval")) { if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold interval; defaulting to %u sec", SPT_THRESHOLD_DEFAULT_INTERVAL); interval = SPT_THRESHOLD_DEFAULT_INTERVAL; continue; } /* 5 --> 99,999 ~= 27h */ if (sscanf(w, "%5u", &interval) != 1) { WARN("Invalid spt-threshold interval %s; defaulting to %u sec", w, SPT_THRESHOLD_DEFAULT_INTERVAL); interval = SPT_THRESHOLD_DEFAULT_INTERVAL; } if (interval < TIMER_INTERVAL) { WARN("Too low spt-threshold interval %s; defaulting to %u sec", w, TIMER_INTERVAL); interval = TIMER_INTERVAL; } continue; } if (EQUAL(w, "packets")) { mode = SPT_PACKETS; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing spt-threshold number of packets; defaulting to %u", SPT_THRESHOLD_DEFAULT_PACKETS); packets = SPT_THRESHOLD_DEFAULT_PACKETS; continue; } /* 10 --> 4294967295, which is max of uint32_t */ if (sscanf(w, "%10u", &packets) != 1) { WARN("Invalid spt-threshold packets %s; defaulting to %u", w, SPT_THRESHOLD_DEFAULT_PACKETS); packets = SPT_THRESHOLD_DEFAULT_INTERVAL; } continue; } if (EQUAL(w, "infinity")) { mode = SPT_INF; continue; } WARN("Invalid spt-threshold parameter %s; reverting to defaults.", w); mode = SPT_THRESHOLD_DEFAULT_MODE; rate = SPT_THRESHOLD_DEFAULT_RATE; packets = SPT_THRESHOLD_DEFAULT_PACKETS; interval = SPT_THRESHOLD_DEFAULT_INTERVAL; break; } spt_threshold.mode = mode; switch (mode) { case SPT_INF: logit(LOG_INFO, 0, "spt-threshold infinity => RP and lasthop router will never switch to SPT."); break; case SPT_RATE: /* Accounting for headers we can approximate 1 byte/s == 10 bits/s (bps) * Note, in the new spt_threshold setting the rate is in kbps as well! */ spt_threshold.bytes = rate * interval / 10 * 1000; spt_threshold.interval = interval; logit(LOG_INFO, 0, "spt-threshold rate %u interval %u", rate, interval); break; case SPT_PACKETS: spt_threshold.packets = packets; spt_threshold.interval = interval; logit(LOG_INFO, 0, "spt-threshold packets %u interval %u", packets, interval); break; } return TRUE; } /** * parse_default_route_metric - Parse default-route-metric option * @s: String token * * Reads and assigns the route metric used for PIM Asserts by default. * This is used if pimd cannot read unicast route metrics from the * OS/kernel. * * Syntax: * default-route-metric <1-1024> * * Default routing protocol distance and route metric statements should * precede all phyint statements in the config file. * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_default_route_metric(char *s) { char *w; u_int value; vifi_t vifi; struct uvif *v; value = UCAST_DEFAULT_ROUTE_METRIC; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing route metric default; defaulting to %u", UCAST_DEFAULT_ROUTE_METRIC); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid route metric default; defaulting to %u", UCAST_DEFAULT_ROUTE_METRIC); value = UCAST_DEFAULT_ROUTE_METRIC; } default_route_metric = value; logit(LOG_INFO, 0, "default-route-metric is %u", value); for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) v->uv_local_metric = default_route_metric; return TRUE; } /** * parse_default_route_distance - Parse default-route-distance option * @s: String token * * Reads and assigns the default source metric preference, i.e. routing * protocol distance. This is used if pimd cannot read unicast routing * protocol information from the OS/kernel. * * Syntax: * default-route-distance <1-255> * * Default routing protocol distance and route metric statements should * precede all phyint statements in the config file. * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ int parse_default_route_distance(char *s) { char *w; u_int value; vifi_t vifi; struct uvif *v; value = UCAST_DEFAULT_ROUTE_DISTANCE; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing default routing protocol distance; defaulting to %u", UCAST_DEFAULT_ROUTE_DISTANCE); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default routing protocol distance; defaulting to %u", UCAST_DEFAULT_ROUTE_DISTANCE); value = UCAST_DEFAULT_ROUTE_DISTANCE; } default_route_distance = value; logit(LOG_INFO, 0, "default-route-distance is %u", value); for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) v->uv_local_pref = default_route_distance; return TRUE; } /** * parse_igmp_query_interval - Parse igmp-query-interval option * @s: String token * * Reads and assigns the default IGMP query interval. If the argument * is missing or invalid the parser defaults to %IGMP_QUERY_INTERVAL * * Syntax: * igmp-query-interval * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_igmp_query_interval(char *s) { char *w; uint32_t value = IGMP_QUERY_INTERVAL; if (EQUAL((w = next_word(&s)), "")) { WARN("Missing argument to igmp-query-interval; defaulting to %u", IGMP_QUERY_INTERVAL); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default igmp-query-interval; defaulting to %u", IGMP_QUERY_INTERVAL); value = IGMP_QUERY_INTERVAL; } igmp_query_interval = value; /* Calculate new querier timeout, or expect config option after this. */ igmp_querier_timeout = 0; return TRUE; } /** * parse_igmp_querier_timeout - Parse igmp-querier-timeout option * @s: String token * * Reads and assigns default querier timeout for an active IGMP querier. * This is the time it takes before pimd tries to take over as the * active querier. If the argument is missing or invalid the system * will calculate a fallback based on the query interval. * * Syntax: * igmp-querier-timeout * * Returns: * When parsing @s is successful this function returns %TRUE, otherwise %FALSE. */ static int parse_igmp_querier_timeout(char *s) { char *w; uint32_t value = 0; uint32_t recommended = QUERIER_TIMEOUT(igmp_query_interval); if (EQUAL((w = next_word(&s)), "")) { WARN("Missing argument to igmp-querier-timeout!"); } else if (sscanf(w, "%u", &value) != 1) { WARN("Invalid default igmp-querier-timeout!"); value = 0; } /* Do some sanity checks to prevent invalid configuration and to recommend * better settings, see GitHub issue troglobit/pimd#31 for details. */ if (value != 0) { /* 1) Prevent invalid configuration */ if (value <= igmp_query_interval) { WARN("IGMP querier timeout %d must be larger than the query interval %d, forcing default!", value, igmp_query_interval); value = recommended; } /* 2) Warn power user of potentially too low setting. */ if (value < recommended) WARN("The IGMP querier timeout %d is smaller than the recommended value %d, allowing ...", value, recommended); logit(LOG_WARNING, 0, "Recommended querier timeout = Robustness x query-interval + response-time / 2 = %d x %d + %d / 2 = %d", IGMP_ROBUSTNESS_VARIABLE, igmp_query_interval, IGMP_QUERY_RESPONSE_INTERVAL, recommended); } igmp_querier_timeout = value; return TRUE; } static void fallback_config(void) { char buf[LINE_BUFSIZ], *s = buf; logit(LOG_NOTICE, 0, "Using built-in defaults, including RP/BSR candidate."); snprintf(buf, sizeof(buf), "priority 20 time 30"); parse_rp_candidate(s); snprintf(buf, sizeof(buf), "priority 5"); parse_bsr_candidate(s); } void config_vifs_from_file(void) { FILE *fp; char linebuf[LINE_BUFSIZ]; char *w, *s; int option; uint8_t *data_ptr; int error_flag; error_flag = FALSE; lineno = 0; /* TODO: HARDCODING!!! */ cand_rp_adv_message.buffer = calloc(1, 4 + sizeof(pim_encod_uni_addr_t) + 255 * sizeof(pim_encod_grp_addr_t)); if (!cand_rp_adv_message.buffer) logit(LOG_ERR, errno, "Ran out of memory in config_vifs_from_file()"); cand_rp_adv_message.prefix_cnt_ptr = cand_rp_adv_message.buffer; /* By default, if no group-prefix configured, then prefix_cnt == 0 * implies group-prefix = 224.0.0.0 and masklen = 4. */ *cand_rp_adv_message.prefix_cnt_ptr = 0; cand_rp_adv_message.insert_data_ptr = cand_rp_adv_message.buffer; /* TODO: XXX: HARDCODING!!! */ cand_rp_adv_message.insert_data_ptr += (4 + 6); fp = fopen(config_file, "r"); if (!fp) { logit(LOG_WARNING, errno, "Cannot open configuration file %s", config_file); fallback_config(); goto nofile; } while (fgets(linebuf, sizeof(linebuf), fp)) { if (strlen(linebuf) >= (LINE_BUFSIZ - 1)) { WARN("Line length must be shorter than %d", LINE_BUFSIZ); error_flag = TRUE; } lineno++; s = linebuf; w = next_word(&s); option = parse_option(w); switch (option) { case CONF_EMPTY: continue; break; case CONF_PHYINT: parse_phyint(s); break; case CONF_CANDIDATE_RP: parse_rp_candidate(s); break; case CONF_RP_ADDRESS: parse_rp_address(s); break; case CONF_GROUP_PREFIX: parse_group_prefix(s); break; case CONF_BOOTSTRAP_RP: parse_bsr_candidate(s); break; case CONF_COMPAT_THRESHOLD: parse_compat_threshold(s); break; case CONF_SPT_THRESHOLD: parse_spt_threshold(s); break; case CONF_DEFAULT_ROUTE_METRIC: parse_default_route_metric(s); break; case CONF_DEFAULT_ROUTE_DISTANCE: parse_default_route_distance(s); break; case CONF_IGMP_QUERY_INTERVAL: parse_igmp_query_interval(s); break; case CONF_IGMP_QUERIER_TIMEOUT: parse_igmp_querier_timeout(s); break; case CONF_HELLO_INTERVAL: parse_hello_interval(s); break; default: logit(LOG_WARNING, 0, "%s:%u - Unknown command '%s'", config_file, lineno, w); error_flag = TRUE; } } fclose(fp); nofile: /* A static RP address is needed for SSM. We use a link-local * address. It is not required to be configured on any interface. */ strncpy(linebuf, "169.254.0.1 232.0.0.0/8\n", sizeof(linebuf)); s = linebuf; parse_rp_address(s); if (error_flag) logit(LOG_ERR, 0, "%s:%u - Syntax error", config_file, lineno); cand_rp_adv_message.message_size = cand_rp_adv_message.insert_data_ptr - cand_rp_adv_message.buffer; if (cand_rp_flag != FALSE) { /* Prepare the RP info */ my_cand_rp_holdtime = 2.5 * my_cand_rp_adv_period; /* TODO: HARDCODING! */ data_ptr = cand_rp_adv_message.buffer + 1; PUT_BYTE(my_cand_rp_priority, data_ptr); PUT_HOSTSHORT(my_cand_rp_holdtime, data_ptr); PUT_EUADDR(my_cand_rp_address, data_ptr); } /* If no IGMP querier timeout was set, calculate from query interval */ if (!igmp_querier_timeout) igmp_querier_timeout = QUERIER_TIMEOUT(igmp_query_interval); IF_DEBUG(DEBUG_IGMP) { logit(LOG_INFO, 0, "IGMP query interval : %u sec", igmp_query_interval); logit(LOG_INFO, 0, "IGMP querier timeout : %u sec", igmp_querier_timeout); } } static uint32_t ifname2addr(char *s) { vifi_t vifi; struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (!strcmp(v->uv_name, s)) return v->uv_lcl_addr; } return 0; } static char *next_word(char **s) { char *w; w = *s; while (*w == ' ' || *w == '\t') w++; *s = w; while (**s != 0) { switch (**s) { case ' ': case '\t': **s = '\0'; (*s)++; return w; case '\n': case '#': **s = '\0'; return w; default: if (isascii((int)**s) && isupper((int)**s)) **s = tolower((int)**s); (*s)++; } } return w; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/configure000077500000000000000000000232641267035112600143700ustar00rootroot00000000000000#!/bin/sh # # --enable-broken-crc: # If your RP is buggy cisco PIM-SMv2 implementation that computes # the PIM-Register checksum over the whole pkt instead only over # the header, you need to define this. Otherwise, all your PIM-Register # may be dropped by the cisco-RP. #DEFS += -DBROKEN_CISCO_CHECKSUM # # --enable-kernel-encap: # Register kernel encapsulation. Your kernel must support registers # kernel encapsulation to be able to use it. #DEFS += -DPIM_REG_KERNEL_ENCAP # # --enable-kernel-mfc: # (*,G) kernel MFC support. Use it ONLY with (*,G) capable kernel #DEFS += -DKERNEL_MFC_WC_G # # --enable-memory-save: # Saves 4 bytes per unconfigured interface per routing entry. If set, # configuring such interface will restart the daemon and will flush # the routing table. #DEFS += -DSAVE_MEMORY # # --enable-scoped-acls: # Scoped access control list support in pimd.conf. If you want to # install NUL OIF for the "scoped groups", use the following syntax: # "phyint IFNAME [scoped masklen ]", e.g. # phyint fxp0 scoped "addr" masklen "len" # Support contributed by Marian Stagarescu #DEFS += -DSCOPED_ACL # OS=`uname` CFG=config.mk TMP=`mktemp /tmp/XXXXXX` BUGREPORT_URL="https://github.com/troglobit/pimd/issues" # Defaults debug=0 disable_genid=0 embedded_libc=0 print() { case $OS in Linux) /bin/echo -ne "$*" ;; *) printf "$*" ;; esac } heading() { echo "# This makefile snippet is generated by the pimd configure script." > $CFG echo >> $CFG echo "# Initial definitions ..." >> $CFG echo "# -D_GNU_SOURCE Use GNU extensions, where possible" >> $CFG echo "# -D_BSD_SOURCE Use functions derived from 4.3 BSD Unix rather than POSIX.1" >> $CFG echo "# In GLIBC >= v2.20 this is replaced with -D_DEFAULT_SOURCE " >> $CFG echo "# -DPIM Enable PIM extensions in RSRR" >> $CFG echo "DEFS = -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_GNU_SOURCE -DPIM" >> $CFG echo "# EXTRA_OBJS = For locally provided objects missing on some platforms, e.g., strlcpy.o" >> $CFG echo "# EXTRA_LIBS = For platform specific libraries, e.g., -lutil" >> $CFG echo >> $CFG echo "# $OS specific settings ..." >> $CFG } usage() { print "Run this script to configure pimd for your system.\n" print "\n" print "Usage: configure [ARG]\n" print "\n" print " --debug Enable debuggable build, -g and -O0\n" print " --prefix=PATH Base installation directory, default: /usr/local/\n" print " --sysconfdir=PATH Path to pimd.conf, default: /etc/\n" print " --embedded-libc Linux system with uClibc or musl libc. These two\n" print " C libraries have strlcpy() and strlcat()" print "\n" print "By default 'make install' installs files to /usr/local/{sbin, share, man} and\n" print "/etc/, this can be changed with the --prefix and --syconfdir options. On top of\n" print "this you can also set DESTDIR when installing, to get another root directory.\n" print "\n" print " --enable-broken-crc Interop with older broken Cisco firmware that did\n" print " checksum in PIM Register messages slightly wrong.\n" print " Should not be needed anymore.\n" print " --enable-kernel-encap Specialized kernels do PIM Register encapsulation\n" print " by themselves, speeding up processing considerably.\n" print " \e[1mNot on Linux/*BSD\e[0m, needs pimkern-PATCH_6!\n" print " --enable-kernel-mfc Specialized kernels may have (*,G) MFC support.\n" print " \e[1mNot on Linux/*BSD\e[0m, need pimkern-PATCH_7!\n" print " --enable-memory-save Save 4 bytes/unconfigured interface/routing entry\n" print " Must restart pimd and cause routing table to be\n" print " flushed when configuring new interfaces.\n" print " --enable-scoped-acls Scoped access control list support in pimd.conf\n" print " phyint IFNAME [scoped masklen ]\n" print " phyint fxp0 scoped 239.0.0.0 masklen 8\n" print " --enable-rsrr Routing Support for Resource Reservations, defined in\n" print " http://tools.ietf.org/html/draft-ietf-rsvp-routing-02\n" print " currently used by RSVP. \e[1mEXPERIMENTAL\e[0m\n" print "\n" print " --disable-pim-genid Disable Generation ID in PIM Hello, RFC3973\n" print " --disable-exit-on-error Do not exit on error messages (LOG_ERR)\n" print " --disable-masklen-check Allow virtual tunctl VIFs with masklen 32\n" print "\n" print " --with-includes=PATH Use this if the multicast header files are not in\n" print " a standard location on your system. E.g., /sys\n" print " --with-max-vifs=MAXVIFS Kernel maximum number of allowed VIFs, default: 32\n" print " \e[1mNote:\e[0m Try multiple routing tables instead!\n" print "\n" print "Report bugs to $BUGREPORT_URL\n" print "\n" exit 1 } echo > $TMP echo "# Configured settings/features ..." >> $TMP while [ "$*" != "" ]; do opt=`expr -- "$1" : "--\([^=]*\)=.*"` arg=`expr -- "$1" : "--[^=]*=\(.*\)"` if [ -z "$opt" ]; then opt=`expr -- "$1" : "--\(.*\)"` if [ -z "$opt" ]; then opt=`expr -- "$1" : "-\(.*\)"` fi fi shift case $opt in debug) debug=1; ;; prefix) echo "prefix = $arg" >> $TMP echo "sysconfdir += $arg/etc" >> $TMP ;; sysconfdir) echo "sysconfdir = $arg" >> $TMP ;; embedded-libc) embedded_libc=1 ;; enable-broken-crc) echo "DEFS += -DBROKEN_CISCO_CHECKSUM" >> $TMP ;; enable-kernel-encap) echo "DEFS += -DPIM_REG_KERNEL_ENCAP" >> $TMP ;; enable-kernel-mfc) echo "DEFS += -DKERNEL_MFC_WC_G" >> $TMP ;; enable-memory-save) echo "DEFS += -DSAVE_MEMORY" >> $TMP ;; enable-scoped-acls) echo "DEFS += -DSCOPED_ACL" >> $TMP ;; enable-rsrr) echo "DEFS += -DRSRR" >> $TMP echo "EXTRA_OBJS += rsrr.o" >> $TMP ;; enable-pim-hello-genid) echo "PIM Hello GenID is enabled by default." ;; disable-pim-genid) disable_genid=1 ;; disable-exit-on-error) echo "DEFS += -DCONTINUE_ON_ERROR" >> $TMP ;; disable-masklen-check) echo "DEFS += -DDISABLE_MASKLEN_CHECK" >> $TMP ;; with-includes) echo "INCLUDES += -I$arg" >> $TMP ;; with-max-vifs) echo "DEFS += -DCUSTOM_MAX_VIFS=$arg" >> $TMP ;; help | h) usage ;; *) echo "Skipping unknown option: $opt" ;; esac done echo "DEFS += -DPACKAGE_BUGREPORT=\\\"$BUGREPORT_URL\\\"" >> $TMP if [ $disable_genid -ne 1 ]; then echo "DEFS += -DENABLE_PIM_HELLO_GENID" >> $TMP fi ## BSDI -D__bsdi__ is defined by the OS #INCLUDES = -Iinclude #DEFS += #EXTRA_OBJS = strlcpy.o pidfile.o ## SunOS, OSF1, gcc #INCLUDES = -Iinclude -Iinclude/sunos-gcc #DEFS += -DSunOS=43 #EXTRA_OBJS = strlcpy.o pidfile.o ## SunOS, OSF1, cc #INCLUDES = -Iinclude -Iinclude/sunos-cc #DEFS += -DSunOS=43 #EXTRA_OBJS = strlcpy.o pidfile.o ## IRIX #INCLUDES = -Iinclude #DEFS += -D_BSD_SIGNALS -DIRIX #EXTRA_OBJS = strlcpy.o pidfile.o ## Solaris 2.5, gcc #INCLUDES = -Iinclude #DEFS += -DSYSV -DSunOS=55 ## Solaris 2.5, cc #INCLUDES = -Iinclude #DEFS += -DSYSV -DSunOS=55 ## Solaris 2.6 #INCLUDES = -Iinclude #DEFS += -DSYSV -DSunOS=56 ## Solaris 2.x #EXTRA_OBJS = strlcpy.o pidfile.o #EXTRA_LIBS = -L/usr/ucblib -lucb -L/usr/lib -lsocket -lnsl case $OS in Linux) heading echo "INCLUDES = -Iinclude" >> $CFG echo "DEFS += -DRAW_OUTPUT_IS_RAW -DIOCTL_OK_ON_RAW_SOCKET" >> $CFG echo "ROUTER_OBJS += netlink.o" >> $CFG if [ $embedded_libc -ne 1 ]; then echo "EXTRA_OBJS = libite/strlcpy.o libite/strlcat.o" >> $CFG fi echo "EXTRA_OBJS += libite/pidfile.o libite/strtonum.o" >> $CFG echo "EXTRA_LIBS =" >> $CFG ;; FreeBSD) heading echo "INCLUDES =" >> $CFG echo "ROUTER_OBJS += routesock.o" >> $CFG echo "EXTRA_OBJS = libite/pidfile.o" >> $CFG echo "EXTRA_LIBS =" >> $CFG ;; NetBSD) heading echo "INCLUDES =" >> $CFG echo "ROUTER_OBJS += routesock.o" >> $CFG echo "EXTRA_OBJS = libite/strtonum.o" >> $CFG echo "EXTRA_LIBS = -lutil" >> $CFG ;; OpenBSD) heading echo "INCLUDES =" >> $CFG echo "ROUTER_OBJS += routesock.o" >> $CFG echo "EXTRA_OBJS =" >> $CFG echo "EXTRA_LIBS = -lutil" >> $CFG ;; *) rm $CFG echo "$OS is currently unsupported. Help out at https://github.com/troglobit/pimd/" exit 1 ;; esac echo >> $CFG echo "# Build specific settings ..." >> $CFG if [ $debug -eq 1 ]; then echo "CFLAGS += -O0 -g" >> $CFG else echo "CFLAGS += -O2" >> $CFG fi cat $TMP >> $CFG rm $TMP if [ ! -e libite/lite.h ]; then print " FIXDEP libite/lite.h missing ... " git submodule update --init >/dev/null if [ $? -ne 0 ]; then print "FAILED! No Internet access?" exit 1 fi print "OK\n" fi exit 0 pimd-2.3.2/debug.c000066400000000000000000000532721267035112600137150ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: debug.c,v 1.22 2001/11/28 00:13:50 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #define SYSLOG_NAMES #include "defs.h" #include #include #define MAX_MSG_SIZE 64 /* Max for dump_frame() */ int log_nmsgs = 0; int loglevel = LOG_NOTICE; unsigned long debug = 0x00000000; /* If (long) is smaller than * 4 bytes, then we are in * trouble. */ static char dumpfilename[] = _PATH_PIMD_DUMP; static char cachefilename[] = _PATH_PIMD_CACHE; /* TODO: notused */ char *packet_kind(int proto, int type, int code) { static char unknown[60]; switch (proto) { case IPPROTO_IGMP: switch (type) { case IGMP_MEMBERSHIP_QUERY: return "IGMP Membership Query "; case IGMP_V1_MEMBERSHIP_REPORT: return "IGMP v1 Membership Report"; case IGMP_V2_MEMBERSHIP_REPORT: return "IGMP v2 Membership Report"; case IGMP_V3_MEMBERSHIP_REPORT: return "IGMP v3 Membership Report"; case IGMP_V2_LEAVE_GROUP: return "IGMP Leave message "; case IGMP_DVMRP: switch (code) { case DVMRP_PROBE: return "DVMRP Neighbor Probe "; case DVMRP_REPORT: return "DVMRP Route Report "; case DVMRP_ASK_NEIGHBORS: return "DVMRP Neighbor Request "; case DVMRP_NEIGHBORS: return "DVMRP Neighbor List "; case DVMRP_ASK_NEIGHBORS2: return "DVMRP Neighbor request 2 "; case DVMRP_NEIGHBORS2: return "DVMRP Neighbor list 2 "; case DVMRP_PRUNE: return "DVMRP Prune message "; case DVMRP_GRAFT: return "DVMRP Graft message "; case DVMRP_GRAFT_ACK: return "DVMRP Graft message ack "; case DVMRP_INFO_REQUEST: return "DVMRP Info Request "; case DVMRP_INFO_REPLY: return "DVMRP Info Reply "; default: snprintf(unknown, sizeof(unknown), "UNKNOWN DVMRP message code = %3d ", code); return unknown; } case IGMP_PIM: /* The old style (PIM v1) encapsulation of PIM messages * inside IGMP messages. */ /* PIM v1 is not implemented but we just inform that a message * has arrived. */ switch (code) { case PIM_V1_QUERY: return "PIM v1 Router-Query "; case PIM_V1_REGISTER: return "PIM v1 Register "; case PIM_V1_REGISTER_STOP: return "PIM v1 Register-Stop "; case PIM_V1_JOIN_PRUNE: return "PIM v1 Join/Prune "; case PIM_V1_RP_REACHABILITY: return "PIM v1 RP-Reachability "; case PIM_V1_ASSERT: return "PIM v1 Assert "; case PIM_V1_GRAFT: return "PIM v1 Graft "; case PIM_V1_GRAFT_ACK: return "PIM v1 Graft_Ack "; default: snprintf(unknown, sizeof(unknown), "UNKNOWN PIM v1 message type =%3d ", code); return unknown; } case IGMP_MTRACE: return "IGMP trace query "; case IGMP_MTRACE_RESP: return "IGMP trace reply "; default: snprintf(unknown, sizeof (unknown), "UNKNOWN IGMP message: type = 0x%02x, code = 0x%02x ", type, code); return unknown; } case IPPROTO_PIM: /* PIM v2 */ switch (type) { case PIM_V2_HELLO: return "PIM v2 Hello "; case PIM_V2_REGISTER: return "PIM v2 Register "; case PIM_V2_REGISTER_STOP: return "PIM v2 Register_Stop "; case PIM_V2_JOIN_PRUNE: return "PIM v2 Join/Prune "; case PIM_V2_BOOTSTRAP: return "PIM v2 Bootstrap "; case PIM_V2_ASSERT: return "PIM v2 Assert "; case PIM_V2_GRAFT: return "PIM-DM v2 Graft "; case PIM_V2_GRAFT_ACK: return "PIM-DM v2 Graft_Ack "; case PIM_V2_CAND_RP_ADV: return "PIM v2 Cand. RP Adv. "; default: snprintf(unknown, sizeof(unknown), "UNKNOWN PIM v2 message type =%3d ", type); return unknown; } default: snprintf(unknown, sizeof(unknown), "UNKNOWN proto =%3d ", proto); return unknown; } } /* * Used for debugging particular type of messages. */ int debug_kind(int proto, int type, int code) { switch (proto) { case IPPROTO_IGMP: switch (type) { case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP; case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP; case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP; case IGMP_V3_MEMBERSHIP_REPORT: return DEBUG_IGMP; case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP; case IGMP_DVMRP: switch (code) { case DVMRP_PROBE: return DEBUG_DVMRP_PEER; case DVMRP_REPORT: return DEBUG_DVMRP_ROUTE; case DVMRP_ASK_NEIGHBORS: return 0; case DVMRP_NEIGHBORS: return 0; case DVMRP_ASK_NEIGHBORS2: return 0; case DVMRP_NEIGHBORS2: return 0; case DVMRP_PRUNE: return DEBUG_DVMRP_PRUNE; case DVMRP_GRAFT: return DEBUG_DVMRP_PRUNE; case DVMRP_GRAFT_ACK: return DEBUG_DVMRP_PRUNE; case DVMRP_INFO_REQUEST: return 0; case DVMRP_INFO_REPLY: return 0; default: return 0; } case IGMP_PIM: /* PIM v1 is not implemented */ switch (code) { case PIM_V1_QUERY: return DEBUG_PIM; case PIM_V1_REGISTER: return DEBUG_PIM; case PIM_V1_REGISTER_STOP: return DEBUG_PIM; case PIM_V1_JOIN_PRUNE: return DEBUG_PIM; case PIM_V1_RP_REACHABILITY: return DEBUG_PIM; case PIM_V1_ASSERT: return DEBUG_PIM; case PIM_V1_GRAFT: return DEBUG_PIM; case PIM_V1_GRAFT_ACK: return DEBUG_PIM; default: return DEBUG_PIM; } case IGMP_MTRACE: return DEBUG_TRACE; case IGMP_MTRACE_RESP: return DEBUG_TRACE; default: return DEBUG_IGMP; } case IPPROTO_PIM: /* PIM v2 */ /* TODO: modify? */ switch (type) { case PIM_V2_HELLO: return DEBUG_PIM; case PIM_V2_REGISTER: return DEBUG_PIM_REGISTER; case PIM_V2_REGISTER_STOP: return DEBUG_PIM_REGISTER; case PIM_V2_JOIN_PRUNE: return DEBUG_PIM; case PIM_V2_BOOTSTRAP: return DEBUG_PIM_BOOTSTRAP; case PIM_V2_ASSERT: return DEBUG_PIM; case PIM_V2_GRAFT: return DEBUG_PIM; case PIM_V2_GRAFT_ACK: return DEBUG_PIM; case PIM_V2_CAND_RP_ADV: return DEBUG_PIM_CAND_RP; default: return DEBUG_PIM; } default: return 0; } return 0; } /* * Some messages are more important than others. This routine * determines the logging level at which to log a send error (often * "No route to host"). This is important when there is asymmetric * reachability and someone is trying to, i.e., mrinfo me periodically. */ int log_level(int proto, int type, int code) { switch (proto) { case IPPROTO_IGMP: switch (type) { case IGMP_MTRACE_RESP: return LOG_INFO; case IGMP_DVMRP: switch (code) { case DVMRP_NEIGHBORS: case DVMRP_NEIGHBORS2: return LOG_INFO; } return LOG_WARNING; case IGMP_PIM: /* PIM v1 */ switch (code) { default: return LOG_INFO; } return LOG_WARNING; default: return LOG_WARNING; } case IPPROTO_PIM: /* PIM v2 */ switch (type) { default: return LOG_INFO; } return LOG_WARNING; default: return LOG_WARNING; } return LOG_WARNING; } /* * Dump internal data structures to a file. */ void fdump(int i __attribute__((unused))) { FILE *fp; fp = fopen(dumpfilename, "w"); if (fp) { dump_vifs(fp); dump_pim_mrt(fp); fclose(fp); } } /* TODO: dummy, to be used in the future. */ /* * Dump local cache contents to a file. */ void cdump(int i __attribute__((unused))) { FILE *fp; fp = fopen(cachefilename, "w"); if (fp) { /* XXX: TODO: implement it: dump_cache(fp); */ fclose(fp); } } /* 1 2 3 4 5 6 7 8 012345678901234567890123456789012345678901234567890123456789012345678901234567890 Virtual Interface Table Vif Local-Address Subnet Thresh Flags Neighbors 0 10.0.3.1 10.0.3/24 1 DR NO-NBR 1 172.16.12.254 172.16.12/24 1 DR PIM 172.16.12.2 172.16.12.3 2 192.168.122.147 register_vif0 1 */ void dump_vifs(FILE *fp) { vifi_t vifi; struct uvif *v; pim_nbr_entry_t *n; int width; int i; struct listaddr *group, *source; fprintf(fp, "Virtual Interface Table ======================================================\n"); fprintf(fp, "Vif Local Address Subnet Thresh Flags Neighbors\n"); fprintf(fp, "--- --------------- ------------------ ------ --------- -----------------\n"); for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { int down = 0; fprintf(fp, "%3u %-15s ", vifi, inet_fmt(v->uv_lcl_addr, s1, sizeof(s1))); if (v->uv_flags & VIFF_REGISTER) fprintf(fp, "%-18s ", v->uv_name); else fprintf(fp,"%-18.18s ", netname(v->uv_subnet, v->uv_subnetmask)); fprintf(fp, "%6u ", v->uv_threshold); /* TODO: XXX: Print VIFF_TUNNEL? */ width = 0; if (v->uv_flags & VIFF_DISABLED) { fprintf(fp, " DISABLED"); down = 1; } if (v->uv_flags & VIFF_DOWN) { fprintf(fp, " DOWN"); down = 1; } if (v->uv_flags & VIFF_DR) { fprintf(fp, " DR"); width += 3; } if (v->uv_flags & VIFF_PIM_NBR) { fprintf(fp, " PIM"); width += 4; } if (v->uv_flags & VIFF_DVMRP_NBR) { fprintf(fp, " DVMRP"); width += 6; } if (v->uv_flags & VIFF_NONBRS) { fprintf(fp, " NO-NBR"); width += 6; } n = v->uv_pim_neighbors; if (!down && n) { for (i = width; i <= 11; i++) fprintf(fp, " "); fprintf(fp, "%-15s\n", inet_fmt(n->address, s1, sizeof(s1))); for (n = n->next; n; n = n->next) fprintf(fp, "%61s%-15s\n", "", inet_fmt(n->address, s1, sizeof(s1))); } else { fprintf(fp, "\n"); } } fprintf(fp, "\n"); /* Dump groups and sources */ fprintf(fp, " %-3s %-15s %-20s", "Vif", "SSM Group", "Sources"); for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { for (group = v->uv_groups; group != NULL; group = group->al_next) { if (IN_PIM_SSM_RANGE(group->al_addr)) { fprintf(fp, "\n %3u %-15s ", vifi, inet_fmt(group->al_addr, s1, sizeof(s1))); for (source = group->al_sources; source != NULL; source = source->al_next) { fprintf(fp, "%s ", inet_fmt(source->al_addr, s1, sizeof(s1))); } } } } fprintf(fp, "\n\n"); } int loglvl(char *level) { int i; for (i = 0; prioritynames[i].c_name; i++) { if (string_match(prioritynames[i].c_name, level)) return prioritynames[i].c_val; } return atoi(level); } /* * Log errors and other messages to the system log daemon and to stderr, * according to the severity of the message and the current debug level. * For errors of severity LOG_ERR or worse, terminate the program. */ void logit(int severity, int syserr, const char *format, ...) { va_list ap; char msg[211]; struct timeval now; struct tm *thyme; time_t lt; va_start(ap, format); vsnprintf(msg, sizeof(msg), format, ap); va_end(ap); /* * Log to stderr if we haven't forked yet and it's a warning or * worse, or if we're debugging. */ if (haveterminal && (debug || severity <= LOG_WARNING)) { gettimeofday(&now, NULL); lt = now.tv_sec; thyme = localtime(<); if (!debug) fprintf(stderr, "%s: ", __progname); fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour, thyme->tm_min, thyme->tm_sec, (long int)(now.tv_usec / 1000), msg); if (syserr) { errno = syserr; fprintf(stderr, ": %m"); } fprintf(stderr, "\n"); } /* * Always log things that are worse than warnings, no matter what * the log_nmsgs rate limiter says. * * Only count things at the defined loglevel or worse in the rate limiter * and exclude debugging (since if you put daemon.debug in syslog.conf * you probably actually want to log the debugging messages so they * shouldn't be rate-limited) */ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) { if ((severity <= loglevel) && (severity != LOG_DEBUG)) log_nmsgs++; if (syserr) { errno = syserr; syslog(severity, "%s: %m", msg); } else { syslog(severity, "%s", msg); } } #ifndef CONTINUE_ON_ERROR if (severity <= LOG_ERR) exit(-1); /* Exit status: 255 */ #endif /* CONTINUE_ON_ERROR */ } /* * Hex dump a control frame to the log, MAX 64 bytes length */ void dump_frame(char *desc, void *dump, size_t len) { unsigned int length, i = 0; unsigned char *data = (unsigned char *)dump; char buf[80] = ""; char tmp[10]; length = len; if (length > MAX_MSG_SIZE) length = MAX_MSG_SIZE; if (desc) logit(LOG_DEBUG, 0, "%s", desc); while (i < length) { if (!(i % 16)) snprintf(buf, sizeof(buf), "%03X: ", i); snprintf(tmp, sizeof(tmp), "%02X ", data[i++]); strlcat(buf, tmp, sizeof(buf)); if (i > 0 && !(i % 16)) logit(LOG_DEBUG, 0, "%s", buf); else if (i > 0 && !(i % 8)) strlcat(buf, ":: ", sizeof(buf)); } logit(LOG_DEBUG, 0, "%s", buf); } static void dump_route(FILE *fp, mrtentry_t *r) { vifi_t vifi; char oifs[(sizeof(vifbitmap_t)<<3)+1]; char joined_oifs[(sizeof(vifbitmap_t)<<3)+1]; char pruned_oifs[(sizeof(vifbitmap_t)<<3)+1]; char leaves_oifs[(sizeof(vifbitmap_t)<<3)+1]; char asserted_oifs[(sizeof(vifbitmap_t)<<3)+1]; char incoming_iif[(sizeof(vifbitmap_t)<<3)+1]; for (vifi = 0; vifi < numvifs; vifi++) { oifs[vifi] = VIFM_ISSET(vifi, r->oifs) ? 'o' : '.'; joined_oifs[vifi] = VIFM_ISSET(vifi, r->joined_oifs) ? 'j' : '.'; pruned_oifs[vifi] = VIFM_ISSET(vifi, r->pruned_oifs) ? 'p' : '.'; leaves_oifs[vifi] = VIFM_ISSET(vifi, r->leaves) ? 'l' : '.'; asserted_oifs[vifi] = VIFM_ISSET(vifi, r->asserted_oifs) ? 'a' : '.'; incoming_iif[vifi] = '.'; } oifs[vifi] = 0x0; /* End of string */ joined_oifs[vifi] = 0x0; pruned_oifs[vifi] = 0x0; leaves_oifs[vifi] = 0x0; asserted_oifs[vifi] = 0x0; incoming_iif[vifi] = 0x0; incoming_iif[r->incoming] = 'I'; /* TODO: don't need some of the flags */ if (r->flags & MRTF_SPT) fprintf(fp, " SPT"); if (r->flags & MRTF_WC) fprintf(fp, " WC"); if (r->flags & MRTF_RP) fprintf(fp, " RP"); if (r->flags & MRTF_REGISTER) fprintf(fp, " REG"); if (r->flags & MRTF_IIF_REGISTER) fprintf(fp, " IIF_REG"); if (r->flags & MRTF_NULL_OIF) fprintf(fp, " NULL_OIF"); if (r->flags & MRTF_KERNEL_CACHE) fprintf(fp, " CACHE"); if (r->flags & MRTF_ASSERTED) fprintf(fp, " ASSERTED"); if (r->flags & MRTF_REG_SUPP) fprintf(fp, " REG_SUPP"); if (r->flags & MRTF_SG) fprintf(fp, " SG"); if (r->flags & MRTF_PMBR) fprintf(fp, " PMBR"); fprintf(fp, "\n"); fprintf(fp, "Joined oifs: %-20s\n", joined_oifs); fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs); fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs); fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs); fprintf(fp, "Outgoing oifs: %-20s\n", oifs); fprintf(fp, "Incoming : %-20s\n", incoming_iif); fprintf(fp, "\nTIMERS: Entry JP RS Assert VIFS:"); for (vifi = 0; vifi < numvifs; vifi++) fprintf(fp, " %d", vifi); fprintf(fp, "\n %5d %4d %4d %6d ", r->timer, r->jp_timer, r->rs_timer, r->assert_timer); for (vifi = 0; vifi < numvifs; vifi++) fprintf(fp, " %2d", r->vif_timers[vifi]); fprintf(fp, "\n"); } void dump_pim_mrt(FILE *fp) { grpentry_t *g; mrtentry_t *r; u_int number_of_cache_mirrors = 0; u_int number_of_groups = 0; cand_rp_t *rp; kernel_cache_t *kc; fprintf(fp, "Multicast Routing Table ======================================================\n"); /* TODO: remove the dummy 0.0.0.0 group (first in the chain) */ for (g = grplist->next; g; g = g->next) { number_of_groups++; r = g->grp_route; if (r) { if (r->flags & MRTF_KERNEL_CACHE) { for (kc = r->kernel_cache; kc; kc = kc->next) number_of_cache_mirrors++; } /* Print the (*,G) routing info */ fprintf(fp, "----------------------------------- (*,G) ------------------------------------\n"); fprintf(fp, "Source Group RP Address Flags\n"); fprintf(fp, "--------------- --------------- --------------- ---------------------------\n"); fprintf(fp, "%-15s ", "INADDR_ANY"); fprintf(fp, "%-15s ", inet_fmt(g->group, s1, sizeof(s1))); fprintf(fp, "%-15s ", IN_PIM_SSM_RANGE(g->group) ? "SSM" : (g->active_rp_grp ? inet_fmt(g->rpaddr, s2, sizeof(s2)) : "NULL")); dump_route(fp, r); } /* Print all (S,G) routing info */ fprintf(fp, "----------------------------------- (S,G) ------------------------------------\n"); for (r = g->mrtlink; r; r = r->grpnext) { if (r->flags & MRTF_KERNEL_CACHE) number_of_cache_mirrors++; /* Print the routing info */ fprintf(fp, "Source Group RP Address Flags\n"); fprintf(fp, "--------------- --------------- --------------- ---------------------------\n"); fprintf(fp, "%-15s ", inet_fmt(r->source->address, s1, sizeof(s1))); fprintf(fp, "%-15s ", inet_fmt(g->group, s2, sizeof(s2))); fprintf(fp, "%-15s ", IN_PIM_SSM_RANGE(g->group) ? "SSM" : (g->active_rp_grp ? inet_fmt(g->rpaddr, s2, sizeof(s2)) : "NULL")); dump_route(fp, r); } }/* for all groups */ /* Print the (*,*,R) routing entries */ fprintf(fp, "--------------------------------- (*,*,G) ------------------------------------\n"); for (rp = cand_rp_list; rp; rp = rp->next) { r = rp->rpentry->mrtlink; if (r) { if (r->flags & MRTF_KERNEL_CACHE) { for (kc = r->kernel_cache; kc; kc = kc->next) number_of_cache_mirrors++; } /* Print the (*,*,RP) routing info */ fprintf(fp, "Source Group RP Address Flags\n"); fprintf(fp, "--------------- --------------- --------------- ---------------------------\n"); fprintf(fp, "%-15s ", inet_fmt(r->source->address, s1, sizeof(s1))); fprintf(fp, "%-15s ", "INADDR_ANY"); fprintf(fp, "%-15s ", ""); dump_route(fp, r); } } /* For all (*,*,RP) */ fprintf(fp, "Number of Groups: %u\n", number_of_groups); fprintf(fp, "Number of Cache MIRRORs: %u\n", number_of_cache_mirrors); fprintf(fp, "------------------------------------------------------------------------------\n\n"); } static void dump_rpgrp(FILE *fp, rp_grp_entry_t *rpgrp, int indent) { grp_mask_t *grp = rpgrp->group; if (indent) fprintf(fp, " "); fprintf(fp, "%-18.18s %-8u %-8u\n", netname(grp->group_addr, grp->group_mask), rpgrp->priority, rpgrp->holdtime); } /* * Dumps the local Cand-RP-set */ int dump_rp_set(FILE *fp) { cand_rp_t *rp; rp_grp_entry_t *rpgrp; fprintf(fp, "Candidate Rendezvous-Point Set ===============================================\n"); fprintf(fp, "RP address Incoming Group Prefix Priority Holdtime\n"); fprintf(fp, "--------------- -------- ------------------ -------- ---------------------\n"); for (rp = cand_rp_list; rp; rp = rp->next) { fprintf(fp, "%-15s %-8d ", inet_fmt(rp->rpentry->address, s1, sizeof(s1)), rp->rpentry->incoming); rpgrp = rp->rp_grp_next; if (rpgrp) { dump_rpgrp(fp, rpgrp, 0); for (rpgrp = rpgrp->rp_grp_next; rpgrp; rpgrp = rpgrp->rp_grp_next) dump_rpgrp(fp, rpgrp, 1); } } fprintf(fp, "------------------------------------------------------------------------------\n"); fprintf(fp, "Current BSR address: %s\n\n", inet_fmt(curr_bsr_address, s1, sizeof(s1))); return TRUE; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/debug.h000066400000000000000000000104101267035112600137050ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: debug.h,v 1.8 2001/09/10 20:31:36 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #ifndef PIMD_DEBUG_H_ #define PIMD_DEBUG_H_ extern unsigned long debug; extern int log_nmsgs; #define IF_DEBUG(l) if (debug && (debug & (l))) #define LOG_MAX_MSGS 100 /* if > 100/minute then shut up for a while */ #define LOG_SHUT_UP 600 /* shut up for 10 minutes */ /* Debug values definition */ /* DVMRP reserved for future use */ #define DEBUG_DVMRP_PRUNE 0x00000001 #define DEBUG_DVMRP_ROUTE 0x00000002 #define DEBUG_DVMRP_PEER 0x00000004 #define DEBUG_DVMRP_TIMER 0x00000008 #define DEBUG_DVMRP_DETAIL 0x01000000 #define DEBUG_DVMRP ( DEBUG_DVMRP_PRUNE | DEBUG_DVMRP_ROUTE | \ DEBUG_DVMRP_PEER ) /* IGMP related */ #define DEBUG_IGMP_PROTO 0x00000010 #define DEBUG_IGMP_TIMER 0x00000020 #define DEBUG_IGMP_MEMBER 0x00000040 #define DEBUG_MEMBER DEBUG_IGMP_MEMBER #define DEBUG_IGMP ( DEBUG_IGMP_PROTO | DEBUG_IGMP_TIMER | \ DEBUG_IGMP_MEMBER ) /* Misc */ #define DEBUG_TRACE 0x00000080 #define DEBUG_TIMEOUT 0x00000100 #define DEBUG_PKT 0x00000200 /* Kernel related */ #define DEBUG_IF 0x00000400 #define DEBUG_KERN 0x00000800 #define DEBUG_MFC 0x00001000 #define DEBUG_RSRR 0x00002000 /* PIM related */ #define DEBUG_PIM_HELLO 0x00004000 #define DEBUG_PIM_REGISTER 0x00008000 #define DEBUG_PIM_JOIN_PRUNE 0x00010000 #define DEBUG_PIM_BOOTSTRAP 0x00020000 #define DEBUG_PIM_ASSERT 0x00040000 #define DEBUG_PIM_CAND_RP 0x00080000 #define DEBUG_PIM_MRT 0x00100000 #define DEBUG_PIM_TIMER 0x00200000 #define DEBUG_PIM_RPF 0x00400000 #define DEBUG_RPF DEBUG_PIM_RPF #define DEBUG_PIM_DETAIL 0x00800000 #define DEBUG_PIM ( DEBUG_PIM_HELLO | DEBUG_PIM_REGISTER | \ DEBUG_PIM_JOIN_PRUNE | DEBUG_PIM_BOOTSTRAP | \ DEBUG_PIM_ASSERT | DEBUG_PIM_CAND_RP | \ DEBUG_PIM_MRT | DEBUG_PIM_TIMER | \ DEBUG_PIM_RPF ) #define DEBUG_MRT ( DEBUG_DVMRP_ROUTE | DEBUG_PIM_MRT ) #define DEBUG_NEIGHBORS ( DEBUG_DVMRP_PEER | DEBUG_PIM_HELLO ) #define DEBUG_TIMER ( DEBUG_IGMP_TIMER | DEBUG_DVMRP_TIMER | \ DEBUG_PIM_TIMER ) #define DEBUG_ASSERT ( DEBUG_PIM_ASSERT ) #define DEBUG_ALL 0xffffffff #define DEBUG_DEFAULT 0xffffffff/* default if "-d" given without value */ int loglvl(char *level); #endif /* PIMD_DEBUG_H_ */ pimd-2.3.2/defs.h000066400000000000000000000606121267035112600135510ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #ifndef __PIMD_DEFS_H__ #define __PIMD_DEFS_H__ #ifndef __linux__ # include /* Defines __BSD_VISIBLE, needed for arc4random() etc. */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ((defined(SYSV)) || (defined(__bsdi__)) || ((defined SunOS) && (SunOS < 50))) #include #endif /* SYSV || bsdi || SunOS 4.x */ #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #endif /* __FreeBSD__ */ #if defined(__bsdi__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define rtentry kernel_rtentry #include #undef rtentry #endif /* bsdi or __FreeBSD_version >= 220000 */ #ifdef __linux__ #define _LINUX_IN_H /* For Linux <= 2.6.25 */ #include #include #else #include #endif /* __linux__ */ /* If using any of the BSD distributions of UNIX the configure script * links with -lutil, but on Linux we link with -lite. All required * APIs are forward declared in lite.h, so we can use it everywhere. */ #include "libite/lite.h" #include #ifdef RSRR #include #endif /* RSRR */ #ifndef BYTE_ORDER #if (BSD >= 199103) #include #else #ifdef __linux__ #include #else #define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */ #define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ #define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */ #if defined(vax) || defined(ns32000) || defined(sun386) || defined(i386) || \ defined(__ia64) || \ defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \ defined(__alpha__) || defined(__alpha) #define BYTE_ORDER LITTLE_ENDIAN #endif #if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\ defined(apollo) || defined(__convex__) || defined(_CRAY) || \ defined(__hppa) || defined(__hp9000) || \ defined(__hp9000s300) || defined(__hp9000s700) || \ defined(BIT_ZERO_ON_LEFT) || defined(m68k) #define BYTE_ORDER BIG_ENDIAN #endif #endif /* linux */ #endif /* BSD */ #endif /* BYTE_ORDER */ typedef void (*cfunc_t) (void *); typedef void (*ihfunc_t) (int, fd_set *); #include "dvmrp.h" /* Added for further compatibility and convenience */ #include "pimd.h" #include "mrt.h" #include "igmpv2.h" #include "igmpv3.h" #include "vif.h" #include "debug.h" #include "pathnames.h" #ifdef RSRR #include "rsrr.h" #include "rsrr_var.h" #endif /* RSRR */ /* * Miscellaneous constants and macros */ #define ENABLINGSTR(val) (val) ? "enabling" : "disabling" /* * Various definitions to make it working for different platforms */ /* The old style sockaddr definition doesn't have sa_len */ #if defined(_AIX) || (defined(BSD) && (BSD >= 199006)) /* sa_len was added with 4.3-Reno */ #define HAVE_SA_LEN #endif /* Versions of Solaris older than 2.6 don't have routing sockets. */ /* XXX TODO: check FreeBSD version and add all other platforms */ #if defined(__linux__) || (defined(SunOS) && SunOS >=56) || \ defined (IRIX) || defined (__bsdi__) || \ defined (__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(NetBSD) || defined(OpenBSD) #define HAVE_ROUTING_SOCKETS 1 #endif /* Older versions of UNIX don't really give us true raw sockets. * Instead, they expect ip_len and ip_off in host byte order, and also * provide them to us in that format when receiving raw frames. * * This list could probably be made longer, e.g., SunOS and __bsdi__ */ #if defined(__NetBSD__) || \ (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) || \ (defined(__OpenBSD__) && (OpenBSD < 200311)) #define HAVE_IP_HDRINCL_BSD_ORDER #endif #define TRUE 1 #define FALSE 0 #ifndef MAX #define MAX(a,b) (((a) >= (b))? (a) : (b)) #define MIN(a,b) (((a) <= (b))? (a) : (b)) #endif /* MAX & MIN */ #define CREATE TRUE #define DONT_CREATE FALSE #define MFC_MOVE_FORCE 0x1 #define MFC_UPDATE_FORCE 0x2 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) #define ARRAY_LEN(a) (sizeof((a)) / sizeof((a)[0])) #define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ #define MINTTL 1 /* min TTL in the packets send locally */ #define MAX_IP_PACKET_LEN 576 #define MIN_IP_HEADER_LEN 20 /* sizeof(struct ip) */ #define IP_IGMP_HEADER_LEN 24 /* MIN + Router Alert */ #define MAX_IP_HEADER_LEN 60 /* * The IGMPv2 defines INADDR_ALLRTRS_GROUP, but earlier * ones don't, so we define it conditionally here. */ #ifndef INADDR_ALLRTRS_GROUP /* address for multicast mtrace msg */ #define INADDR_ALLRTRS_GROUP (uint32_t)0xe0000002 /* 224.0.0.2 */ #endif #ifndef INADDR_ALLRPTS_GROUP #define INADDR_ALLRPTS_GROUP ((in_addr_t)0xe0000016) /* 224.0.0.22, IGMPv3 */ #endif #ifndef INADDR_MAX_LOCAL_GROUP #define INADDR_MAX_LOCAL_GROUP (uint32_t)0xe00000ff /* 224.0.0.255 */ #endif #define INADDR_ANY_N (uint32_t)0x00000000 /* INADDR_ANY in * network order */ #define CLASSD_PREFIX (uint32_t)0xe0000000 /* 224.0.0.0 */ #define STAR_STAR_RP_MSKLEN 4 /* Masklen for * 224.0.0.0 : * to encode (*,*,RP) */ #define ALL_MCAST_GROUPS_ADDR (uint32_t)0xe0000000 /* 224.0.0.0 */ #define ALL_MCAST_GROUPS_LEN 4 /* Used by DVMRP */ #define DEFAULT_METRIC 1 /* default subnet/tunnel metric */ #define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */ /* Used if no relaible unicast routing information available */ #define UCAST_DEFAULT_ROUTE_DISTANCE 101 #define UCAST_DEFAULT_ROUTE_METRIC 1024 #define TIMER_INTERVAL 5 /* 5 sec virtual timer granularity */ /* * TODO: recalculate the messages sizes, probably with regard to the MTU * TODO: cleanup */ #define MAX_JP_MESSAGE_SIZE 8192 #define MAX_JP_MESSAGE_POOL_NUMBER 8 #define MAX_JOIN_LIST_SIZE 1500 #define MAX_PRUNE_LIST_SIZE 1500 #ifdef RSRR #define BIT_ZERO(X) ((X) = 0) #define BIT_SET(X,n) ((X) |= 1 << (n)) #define BIT_CLR(X,n) ((X) &= ~(1 << (n))) #define BIT_TST(X,n) ((X) & 1 << (n)) #endif /* RSRR */ #ifndef __linux__ #define RANDOM() arc4random() #else #define RANDOM() (uint32_t)random() #endif /* NetBSD 6.1, for instance, does not have IPOPT_RA defined. */ #ifndef IPOPT_RA #define IPOPT_RA 148 #endif /* * External declarations for global variables and functions. */ #define SEND_BUF_SIZE (128*1024) /* Maximum buff size to * send a packet */ #define RECV_BUF_SIZE (128*1024) /* Maximum buff size to * receive a packet */ #define SO_SEND_BUF_SIZE_MAX (256*1024) #define SO_SEND_BUF_SIZE_MIN (48*1024) #define SO_RECV_BUF_SIZE_MAX (256*1024) #define SO_RECV_BUF_SIZE_MIN (48*1024) /* * Global settings, from config.c */ extern uint16_t pim_timer_hello_interval; extern uint16_t pim_timer_hello_holdtime; /* TODO: describe the variables and clean up */ extern char *igmp_recv_buf; extern char *igmp_send_buf; extern char *pim_recv_buf; extern char *pim_send_buf; extern int igmp_socket; extern int pim_socket; extern uint32_t allhosts_group; extern uint32_t allrouters_group; extern uint32_t allreports_group; extern uint32_t allpimrouters_group; extern build_jp_message_t *build_jp_message_pool; extern int build_jp_message_pool_counter; extern uint32_t virtual_time; extern char *config_file; extern int haveterminal; extern char *__progname; extern struct cand_rp_adv_message_ { uint8_t *buffer; uint8_t *insert_data_ptr; uint8_t *prefix_cnt_ptr; uint16_t message_size; } cand_rp_adv_message; extern int disable_all_by_default; extern int mrt_table_id; /* * Used to contol the switching to the shortest path: */ typedef enum { SPT_RATE, SPT_PACKETS, SPT_INF } spt_mode_t; typedef struct { uint8_t mode; uint32_t bytes; uint32_t packets; uint32_t interval; } spt_threshold_t; extern spt_threshold_t spt_threshold; extern cand_rp_t *cand_rp_list; extern grp_mask_t *grp_mask_list; extern cand_rp_t *segmented_cand_rp_list; extern grp_mask_t *segmented_grp_mask_list; extern uint16_t curr_bsr_fragment_tag; extern uint8_t curr_bsr_priority; extern uint32_t curr_bsr_address; extern uint32_t curr_bsr_hash_mask; extern uint8_t cand_bsr_flag; /* candidate BSR flag */ extern uint8_t my_bsr_priority; extern uint32_t my_bsr_address; extern uint32_t my_bsr_hash_mask; extern uint8_t cand_rp_flag; /* Candidate RP flag */ extern uint32_t my_cand_rp_address; extern uint8_t my_cand_rp_priority; extern uint16_t my_cand_rp_holdtime; extern uint16_t my_cand_rp_adv_period; /* The locally configured * Cand-RP adv. period. */ extern uint16_t pim_bootstrap_timer; extern uint32_t rp_my_ipv4_hashmask; extern uint16_t pim_cand_rp_adv_timer; /* route.c */ extern uint32_t default_route_metric; extern uint32_t default_route_distance; /* igmp_proto.c */ extern uint32_t igmp_query_interval; extern uint32_t igmp_querier_timeout; /* mrt.c */ extern srcentry_t *srclist; extern grpentry_t *grplist; /* vif.c */ extern struct uvif uvifs[MAXVIFS]; extern vifi_t numvifs; extern int total_interfaces; extern vifi_t reg_vif_num; extern int phys_vif; extern int udp_socket; extern int vifs_down; #define MAX_INET_BUF_LEN 19 extern char s1[MAX_INET_BUF_LEN]; extern char s2[MAX_INET_BUF_LEN]; extern char s3[MAX_INET_BUF_LEN]; extern char s4[MAX_INET_BUF_LEN]; #if !((defined(BSD) && (BSD >= 199103)) || (defined(__linux__))) extern int errno; #endif #ifndef IGMP_MEMBERSHIP_QUERY #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY #if !(defined(NetBSD) || defined(OpenBSD) || defined(__FreeBSD__)) #define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT #else #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT #endif #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE #endif #if defined(__FreeBSD__) /* From FreeBSD 8.x */ #define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT #else #define IGMP_V3_MEMBERSHIP_REPORT 0x22 /* Ver. 3 membership report */ #endif #if defined(NetBSD) || defined(OpenBSD) || defined(__FreeBSD__) #define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY #define IGMP_MTRACE IGMP_MTRACE_QUERY #endif /* For timeout. The timers count down */ #define SET_TIMER(timer, value) (timer) = (value) #define RESET_TIMER(timer) (timer) = 0 #define COPY_TIMER(timer_1, timer_2) (timer_2) = (timer_1) #define IF_TIMER_SET(timer) if ((timer) > 0) #define IF_TIMER_NOT_SET(timer) if ((timer) <= 0) #define FIRE_TIMER(timer) (timer) = 0 #define IF_TIMEOUT(timer) \ if (!((timer) -= (MIN(timer, TIMER_INTERVAL)))) #define IF_NOT_TIMEOUT(timer) \ if ((timer) -= (MIN(timer, TIMER_INTERVAL))) #define TIMEOUT(timer) \ (!((timer) -= (MIN(timer, TIMER_INTERVAL)))) #define NOT_TIMEOUT(timer) \ ((timer) -= (MIN(timer, TIMER_INTERVAL))) #define ELSE else /* To make emacs cc-mode happy */ #define MASK_TO_VAL(x, i) { \ uint32_t _x = ntohl(x); \ (i) = 1; \ while ((_x) <<= 1) \ (i)++; \ }; #define VAL_TO_MASK(x, i) { \ x = htonl(~((1 << (32 - (i))) - 1)); \ }; /* * External function definitions */ /* callout.c */ extern void callout_init (void); extern void free_all_callouts (void); extern void age_callout_queue (int); extern int timer_nextTimer (void); extern int timer_setTimer (int, cfunc_t, void *); extern void timer_clearTimer (int); extern int timer_leftTimer (int); /* config.c */ extern void config_vifs_from_kernel (void); extern void config_vifs_from_file (void); /* debug.c */ extern char *packet_kind (int proto, int type, int code); extern int debug_kind (int proto, int type, int code); extern void logit (int, int, const char *, ...); extern void dump_frame (char *desc, void *dump, size_t len); extern int log_level (int proto, int type, int code); extern void dump (int i); extern void fdump (int i); extern void cdump (int i); extern void dump_vifs (FILE *fp); extern void dump_pim_mrt (FILE *fp); extern int dump_rp_set (FILE *fp); /* dvmrp_proto.c */ extern void dvmrp_accept_probe (uint32_t src, uint32_t dst, uint8_t *p, int datalen, uint32_t level); extern void dvmrp_accept_report (uint32_t src, uint32_t dst, uint8_t *p, int datalen, uint32_t level); extern void dvmrp_accept_info_request (uint32_t src, uint32_t dst, uint8_t *p, int datalen); extern void dvmrp_accept_info_reply (uint32_t src, uint32_t dst, uint8_t *p, int datalen); extern void dvmrp_accept_neighbors (uint32_t src, uint32_t dst, uint8_t *p, int datalen, uint32_t level); extern void dvmrp_accept_neighbors2 (uint32_t src, uint32_t dst, uint8_t *p, int datalen, uint32_t level); extern void dvmrp_accept_prune (uint32_t src, uint32_t dst, uint8_t *p, int datalen); extern void dvmrp_accept_graft (uint32_t src, uint32_t dst, uint8_t *p, int datalen); extern void dvmrp_accept_g_ack (uint32_t src, uint32_t dst, uint8_t *p, int datalen); /* igmp.c */ extern void init_igmp (void); extern void send_igmp (char *buf, uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen); /* igmp_proto.c */ extern void query_groups (struct uvif *v); extern void accept_membership_query (uint32_t src, uint32_t dst, uint32_t group, int tmo, int igmp_version); extern void accept_group_report (uint32_t src, uint32_t dst, uint32_t group, int r_type); extern void accept_leave_message (uint32_t src, uint32_t dst, uint32_t group); extern void accept_membership_report(uint32_t src, uint32_t dst, struct igmpv3_report *report, ssize_t reportlen); /* inet.c */ extern int inet_cksum (uint16_t *addr, u_int len); extern int inet_valid_host (uint32_t naddr); extern int inet_valid_mask (uint32_t mask); extern int inet_valid_subnet (uint32_t nsubnet, uint32_t nmask); extern char *inet_fmt (uint32_t addr, char *s, size_t len); extern char *netname (uint32_t addr, uint32_t mask); extern uint32_t inet_parse (char *s, int n); /* kern.c */ extern void k_set_sndbuf (int socket, int bufsize, int minsize); extern void k_set_rcvbuf (int socket, int bufsize, int minsize); extern void k_hdr_include (int socket, int val); extern void k_set_ttl (int socket, int t); extern void k_set_loop (int socket, int l); extern void k_set_if (int socket, uint32_t ifa); extern void k_set_router_alert (int socket); extern void k_join (int socket, uint32_t grp, struct uvif *v); extern void k_leave (int socket, uint32_t grp, struct uvif *v); extern void k_init_pim (int socket); extern void k_stop_pim (int socket); extern int k_del_mfc (int socket, uint32_t source, uint32_t group); extern int k_chg_mfc (int socket, uint32_t source, uint32_t group, vifi_t iif, vifbitmap_t oifs, uint32_t rp_addr); extern void k_add_vif (int socket, vifi_t vifi, struct uvif *v); extern void k_del_vif (int socket, vifi_t vifi, struct uvif *v); extern int k_get_vif_count (vifi_t vifi, struct vif_count *retval); extern int k_get_sg_cnt (int socket, uint32_t source, uint32_t group, struct sg_count *retval); /* main.c */ extern int register_input_handler (int fd, ihfunc_t func); /* mrt.c */ extern void init_pim_mrt (void); extern mrtentry_t *find_route (uint32_t source, uint32_t group, uint16_t flags, char create); extern grpentry_t *find_group (uint32_t group); extern srcentry_t *find_source (uint32_t source); extern void delete_mrtentry (mrtentry_t *mrtentry_ptr); extern void delete_srcentry (srcentry_t *srcentry_ptr); extern void delete_grpentry (grpentry_t *grpentry_ptr); extern void delete_mrtentry_all_kernel_cache (mrtentry_t *mrtentry_ptr); extern void delete_single_kernel_cache (mrtentry_t *mrtentry_ptr, kernel_cache_t *kernel_cache_ptr); extern void delete_single_kernel_cache_addr (mrtentry_t *mrtentry_ptr, uint32_t source, uint32_t group); extern void add_kernel_cache (mrtentry_t *mrtentry_ptr, uint32_t source, uint32_t group, uint16_t flags); /* pim.c */ extern void init_pim (void); extern void send_pim (char *buf, uint32_t src, uint32_t dst, int type, size_t len); extern void send_pim_unicast (char *buf, int mtu, uint32_t src, uint32_t dst, int type, size_t len); /* pim_proto.c */ extern int receive_pim_hello (uint32_t src, uint32_t dst, char *msg, size_t len); extern int send_pim_hello (struct uvif *v, uint16_t holdtime); extern void delete_pim_nbr (pim_nbr_entry_t *nbr_delete); extern int receive_pim_register (uint32_t src, uint32_t dst, char *msg, size_t len); extern int send_pim_null_register (mrtentry_t *r); extern int receive_pim_register_stop (uint32_t src, uint32_t dst, char *msg, size_t len); extern int send_pim_register (char *pkt); extern int receive_pim_join_prune (uint32_t src, uint32_t dst, char *msg, size_t len); extern int join_or_prune (mrtentry_t *mrtentry_ptr, pim_nbr_entry_t *upstream_router); extern int receive_pim_assert (uint32_t src, uint32_t dst, char *msg, size_t len); extern int send_pim_assert (uint32_t source, uint32_t group, vifi_t vifi, mrtentry_t *mrtentry_ptr); extern int send_periodic_pim_join_prune (vifi_t vifi, pim_nbr_entry_t *pim_nbr, uint16_t holdtime); extern int add_jp_entry (pim_nbr_entry_t *pim_nbr, uint16_t holdtime, uint32_t group, uint8_t grp_msklen, uint32_t source, uint8_t src_msklen, uint16_t addr_flags, uint8_t join_prune); extern void pack_and_send_jp_message (pim_nbr_entry_t *pim_nbr); extern int receive_pim_cand_rp_adv (uint32_t src, uint32_t dst, char *msg, size_t len); extern int receive_pim_bootstrap (uint32_t src, uint32_t dst, char *msg, size_t len); extern int send_pim_cand_rp_adv (void); extern void send_pim_bootstrap (void); /* route.c */ extern int set_incoming (srcentry_t *srcentry_ptr, int srctype); extern vifi_t get_iif (uint32_t source); extern pim_nbr_entry_t *find_pim_nbr (uint32_t source); extern int add_sg_oif (mrtentry_t *mrtentry_ptr, vifi_t vifi, uint16_t holdtime, int update_holdtime); extern void add_leaf (vifi_t vifi, uint32_t source, uint32_t group); extern void delete_leaf (vifi_t vifi, uint32_t source, uint32_t group); extern int change_interfaces (mrtentry_t *mrtentry_ptr, vifi_t new_iif, vifbitmap_t new_joined_oifs_, vifbitmap_t new_pruned_oifs, vifbitmap_t new_leaves_, vifbitmap_t new_asserted_oifs, uint16_t flags); extern void calc_oifs (mrtentry_t *mrtentry_ptr, vifbitmap_t *oifs_ptr); extern void process_kernel_call (void); extern int delete_vif_from_mrt (vifi_t vifi); extern mrtentry_t *switch_shortest_path (uint32_t source, uint32_t group); /* routesock.c and netlink.c */ extern int k_req_incoming (uint32_t source, struct rpfctl *rpfp); extern int init_routesock (void); extern int routing_socket; /* rp.c */ extern void init_rp_and_bsr (void); extern uint16_t bootstrap_initial_delay (void); extern rp_grp_entry_t *add_rp_grp_entry (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t rp_addr, uint8_t rp_priority, uint16_t rp_holdtime, uint32_t group_addr, uint32_t group_mask, uint32_t bsr_hash_mask, uint16_t fragment_tag); extern void delete_rp_grp_entry (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, rp_grp_entry_t *rp_grp_entry_delete); extern void delete_grp_mask (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t group_addr, uint32_t group_mask); extern void delete_rp (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t rp_addr); extern void delete_rp_list (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list); extern rpentry_t *rp_match (uint32_t group); extern rp_grp_entry_t *rp_grp_match (uint32_t group); extern rpentry_t *rp_find (uint32_t rp_address); extern int remap_grpentry (grpentry_t *grpentry_ptr); extern int create_pim_bootstrap_message (char *send_buff); extern int check_mrtentry_rp (mrtentry_t *mrtentry_ptr, uint32_t rp_addr); #ifdef RSRR #ifdef PIM #define gtable mrtentry #endif /* PIM */ #define RSRR_NOTIFICATION_OK TRUE #define RSRR_NOTIFICATION_FALSE FALSE /* rsrr.c */ extern void rsrr_init (void); extern void rsrr_clean (void); extern void rsrr_cache_send (struct gtable *, int); extern void rsrr_cache_clean (struct gtable *); extern void rsrr_cache_bring_up (struct gtable *); #endif /* RSRR */ /* timer.c */ extern void init_timers (void); extern void age_vifs (void); extern void age_routes (void); extern void age_misc (void); extern int unicast_routing_changes (srcentry_t *src_ent); extern int clean_srclist (void); /* trace.c */ /* u_int is promoted uint8_t */ extern void accept_mtrace (uint32_t src, uint32_t dst, uint32_t group, char *data, u_int no, int datalen); extern void accept_neighbor_request (uint32_t src, uint32_t dst); extern void accept_neighbor_request2 (uint32_t src, uint32_t dst); /* vif.c */ extern void init_vifs (void); extern void zero_vif (struct uvif *, int); extern void stop_all_vifs (void); extern void check_vif_state (void); extern vifi_t local_address (uint32_t src); extern vifi_t find_vif_direct (uint32_t src); extern vifi_t find_vif_direct_local (uint32_t src); extern uint32_t max_local_address (void); struct rp_hold { struct rp_hold *next; uint32_t address; uint32_t group; uint32_t mask; uint8_t priority; }; #endif /* __PIMD_DEFS_H__ */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/dvmrp.h000066400000000000000000000162531267035112600137620ustar00rootroot00000000000000/* * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * dvmrp.h,v 3.8.4.3 1997/03/14 00:28:47 fenner Exp */ /* * A DVMRP message consists of an IP header + an IGMP header + (for some types) * zero or more bytes of data. * * For REPORT messages, the data is route information; the route information * consists of one or more lists of the following form: * * (mask, (origin, metric), (origin, metric), ...) * * where: * * "mask" is the subnet mask for all the origins in the list. * It is always THREE bytes long, containing the low-order * three bytes of the mask (the high-order byte is always * 0xff and therefore need not be transmitted). * * "origin" is the number of a subnet from which multicast datagrams * may originate. It is from one to four bytes long, * depending on the value of "mask": * if all bytes of the mask are zero * the subnet number is one byte long * else if the low-order two bytes of the mask are zero * the subnet number is two bytes long * else if the lowest-order byte of the mask is zero * the subnet number is three bytes long, * else * the subnet number is four bytes long. * * "metric" is a one-byte value consisting of two subfields: * - the high-order bit is a flag which, when set, indicates * the last (origin, metric) pair of a list. * - the low-order seven bits contain the routing metric for * the corresponding origin, relative to the sender of the * DVMRP report. The metric may have the value of UNREACHABLE * added to it as a "split horizon" indication (so called * "poisoned reverse"). * * Within a list, the origin subnet numbers must be in ascending order, and * the lists themselves are in order of increasing mask value. A message may * not exceed 576 bytes, the default maximum IP reassembly size, including * the IP and IGMP headers; the route information may be split across more * than one message if necessary, by terminating a list in one message and * starting a new list in the next message (repeating the same mask value, * if necessary). * * For NEIGHBORS messages, the data is neighboring-router information * consisting of one or more lists of the following form: * * (local-addr, metric, threshold, ncount, neighbor, neighbor, ...) * * where: * * "local-addr" is the sending router's address as seen by the neighbors * in this list; it is always four bytes long. * "metric" is a one-byte unsigned value, the TTL `cost' of forwarding * packets to any of the neighbors on this list. * "threshold" is a one-byte unsigned value, a lower bound on the TTL a * packet must have to be forwarded to any of the neighbors on * this list. * "ncount" is the number of neighbors in this list. * "neighbor" is the address of a neighboring router, four bytes long. * * As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes, * including the IP and IGMP headers; split longer messages by terminating the * list in one and continuing in another, repeating the local-addr, etc., if * necessary. * * For NEIGHBORS2 messages, the data is identical to NEIGHBORS except * there is a flags byte before the neighbor count: * * (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...) */ /* * DVMRP message types (carried in the "code" field of an IGMP header) */ #define DVMRP_PROBE 1 /* for finding neighbors */ #define DVMRP_REPORT 2 /* for reporting some or all routes */ #define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */ /* of this router's neighbors. */ #define DVMRP_NEIGHBORS 4 /* response to such a request */ #define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */ #define DVMRP_NEIGHBORS2 6 #define DVMRP_PRUNE 7 /* prune message */ #define DVMRP_GRAFT 8 /* graft message */ #define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */ #define DVMRP_INFO_REQUEST 10 /* information request */ #define DVMRP_INFO_REPLY 11 /* information reply */ /* * 'flags' byte values in DVMRP_NEIGHBORS2 reply. */ #define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */ #define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */ #define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */ #define DVMRP_NF_DOWN 0x10 /* kernel state of interface */ #define DVMRP_NF_DISABLED 0x20 /* administratively disabled */ #define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */ #define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf */ /* * Request/reply types for info queries/replies */ #define DVMRP_INFO_VERSION 1 /* version string */ #define DVMRP_INFO_NEIGHBORS 2 /* neighbors2 data */ /* * Limit on length of route data */ /* TODO: now in defs.h #define MAX_IP_PACKET_LEN 576 #define MIN_IP_HEADER_LEN 20 #define MAX_IP_HEADER_LEN 60 */ #define MAX_DVMRP_DATA_LEN \ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN ) /* * Various protocol constants (all times in seconds) */ /* address for multicast DVMRP msgs */ #define INADDR_DVMRP_GROUP (uint32_t)0xe0000004 /* 224.0.0.4 */ #define DVMRP_ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */ /* (This is the timer interrupt */ /* interval; all times must be */ /* multiples of this value.) */ #define DVMRP_ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */ #define DVMRP_ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */ #define DVMRP_ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */ #define DVMRP_ROUTE_DISCARD_TIME 340 /* time to garbage collect route */ #define DVMRP_LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */ #define DVMRP_NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */ #define DVMRP_NEIGHBOR_EXPIRE_TIME 30 /* time to consider neighbor gone */ #define DVMRP_OLD_NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */ #define DVMRP_UNREACHABLE 32 /* "infinity" metric, must be <= 64 */ /* TODO: remove the DVMRP prefix and merge it with the PIM code? */ #define DVMRP_MAX_RATE_LIMIT 100000 /* max rate limit */ #define DVMRP_DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */ #define DVMRP_DEFAULT_TUN_RATE_LIMIT 500 /* default tunnel rate limit */ #define DVMRP_DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */ #define DVMRP_MIN_CACHE_LIFETIME 60 /* minimum allowed cache lifetime */ #define DVMRP_AVERAGE_PRUNE_LIFETIME 7200 /* average lifetime of prunes sent */ #define DVMRP_MIN_PRUNE_LIFETIME 120 /* minimum allowed prune lifetime */ #define DVMRP_GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */ #define DVMRP_PRUNE_REXMIT_VAL 3 /* initial time for prune rexmission*/ #define DVMRP_OLD_AGE_THRESHOLD 2 /* # of query intervals to remember */ /* presence of IGMPv1 member */ /* XXX NOTE that this technically */ /* violates IGMPv2 draft as the */ /* timer is 5 seconds too short */ pimd-2.3.2/dvmrp_proto.c000066400000000000000000000132011267035112600151660ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: dvmrp_proto.c,v 1.10 2001/09/10 20:31:36 pavlin Exp $ */ #include "defs.h" /* TODO */ /* * Process an incoming neighbor probe message. */ void dvmrp_accept_probe(src, dst, p, datalen, level) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); uint32_t level __attribute__((unused)); { return; } /* TODO */ /* * Process an incoming route report message. */ void dvmrp_accept_report(src, dst, p, datalen, level) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); uint32_t level __attribute__((unused)); { return; } /* TODO */ void dvmrp_accept_info_request(src, dst, p, datalen) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); { return; } /* * Process an incoming info reply message. */ void dvmrp_accept_info_reply(src, dst, p, datalen) uint32_t src; uint32_t dst; uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); { IF_DEBUG(DEBUG_PKT) logit(LOG_DEBUG, 0, "ignoring spurious DVMRP info reply from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } /* * Process an incoming neighbor-list message. */ void dvmrp_accept_neighbors(src, dst, p, datalen, level) uint32_t src; uint32_t dst; uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); uint32_t level __attribute__((unused)); { logit(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } /* * Process an incoming neighbor-list message. */ void dvmrp_accept_neighbors2(src, dst, p, datalen, level) uint32_t src; uint32_t dst; uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); uint32_t level __attribute__((unused)); { IF_DEBUG(DEBUG_PKT) logit(LOG_DEBUG, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } /* TODO */ /* * Takes the prune message received and then strips it to * determine the (src, grp) pair to be pruned. * * Adds the router to the (src, grp) entry then. * * Determines if further packets have to be sent down that vif * * Determines if a corresponding prune message has to be generated */ void dvmrp_accept_prune(src, dst, p, datalen) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); { return; } /* TODO */ /* determine the multicast group and src * * if it does, then determine if a prune was sent * upstream. * if prune sent upstream, send graft upstream and send * ack downstream. * * if no prune sent upstream, change the forwarding bit * for this interface and send ack downstream. * * if no entry exists for this group send ack downstream. */ void dvmrp_accept_graft(src, dst, p, datalen) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); { return; } /* TODO */ /* * find out which group is involved first of all * then determine if a graft was sent. * if no graft sent, ignore the message * if graft was sent and the ack is from the right * source, remove the graft timer so that we don't * have send a graft again */ void dvmrp_accept_g_ack(src, dst, p, datalen) uint32_t src __attribute__((unused)); uint32_t dst __attribute__((unused)); uint8_t *p __attribute__((unused)); int datalen __attribute__((unused)); { return; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/igmp.c000066400000000000000000000367341267035112600135670ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: igmp.c,v 1.18 2002/09/26 00:59:29 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" /* * Exported variables. */ char *igmp_recv_buf; /* input packet buffer */ char *igmp_send_buf; /* output packet buffer */ int igmp_socket; /* socket for all network I/O */ uint32_t allhosts_group; /* allhosts addr in net order */ uint32_t allrouters_group; /* All-Routers addr in net order */ uint32_t allreports_group; /* All IGMP routers in net order */ #ifdef RAW_OUTPUT_IS_RAW extern int curttl; #endif /* RAW_OUTPUT_IS_RAW */ /* * Local functions definitions. */ static void igmp_read (int i, fd_set *rfd); static void accept_igmp (ssize_t recvlen); /* * Open and initialize the igmp socket, and fill in the non-changing * IP header fields in the output packet buffer. */ void init_igmp(void) { struct ip *ip; char *router_alert; igmp_recv_buf = calloc(1, RECV_BUF_SIZE); igmp_send_buf = calloc(1, SEND_BUF_SIZE); if (!igmp_recv_buf || !igmp_send_buf) logit(LOG_ERR, 0, "Ran out of memory in init_igmp()"); if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) logit(LOG_ERR, errno, "Failed creating IGMP socket in init_igmp()"); k_hdr_include(igmp_socket, TRUE); /* include IP header when sending */ k_set_sndbuf(igmp_socket, SO_SEND_BUF_SIZE_MAX, SO_SEND_BUF_SIZE_MIN); /* lots of output buffering */ k_set_rcvbuf(igmp_socket, SO_RECV_BUF_SIZE_MAX, SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */ k_set_ttl(igmp_socket, MINTTL); /* restrict multicasts to one hop */ k_set_loop(igmp_socket, FALSE); /* disable multicast loopback */ ip = (struct ip *)igmp_send_buf; memset(ip, 0, IP_IGMP_HEADER_LEN); ip->ip_v = IPVERSION; ip->ip_hl = IP_IGMP_HEADER_LEN >> 2; ip->ip_tos = 0xc0; /* Internet Control */ ip->ip_id = 0; /* let kernel fill in */ ip->ip_off = 0; ip->ip_ttl = MAXTTL; /* applies to unicasts only */ ip->ip_p = IPPROTO_IGMP; ip->ip_sum = 0; /* let kernel fill in */ /* Enable RFC2113 IP Router Alert. Per spec this is required to * force certain routers/switches to inspect this frame. */ router_alert = igmp_send_buf + sizeof(struct ip); router_alert[0] = IPOPT_RA; router_alert[1] = 4; router_alert[2] = 0; router_alert[3] = 0; /* Everywhere in the daemon we use network-byte-order */ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); allrouters_group = htonl(INADDR_ALLRTRS_GROUP); allreports_group = htonl(INADDR_ALLRPTS_GROUP); if (register_input_handler(igmp_socket, igmp_read) < 0) logit(LOG_ERR, 0, "Failed registering igmp_read() as an input handler in init_igmp()"); } /* Read an IGMP message */ static void igmp_read(int i __attribute__((unused)), fd_set *rfd __attribute__((unused))) { ssize_t len; socklen_t dummy = 0; while ((len = recvfrom(igmp_socket, igmp_recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_ERR, errno, "Failed recvfrom() in igmp_read()"); return; } accept_igmp(len); } /* * Process a newly received IGMP packet that is sitting in the input * packet buffer. */ static void accept_igmp(ssize_t recvlen) { int ipdatalen, iphdrlen, igmpdatalen; uint32_t src, dst, group; struct ip *ip; struct igmp *igmp; int igmp_version = 3; if (recvlen < (ssize_t)sizeof(struct ip)) { logit(LOG_WARNING, 0, "Received IGMP packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)igmp_recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; /* packets sent up from kernel to daemon have ip->ip_p = 0 */ if (ip->ip_p == 0) { #if 0 /* XXX */ if (src == 0 || dst == 0) logit(LOG_WARNING, 0, "Kernel request not accurate, src %s dst %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); else #endif process_kernel_call(); return; } iphdrlen = ip->ip_hl << 2; #if 0 #ifdef HAVE_IP_HDRINCL_BSD_ORDER #ifdef __NetBSD__ ipdatalen = ip->ip_len; /* The NetBSD kernel subtracts hlen for us, unfortunately. */ #else ipdatalen = ip->ip_len - iphdrlen; #endif #else ipdatalen = ntohs(ip->ip_len) - iphdrlen; #endif #else /* !0 */ ipdatalen = recvlen - iphdrlen; #endif /* O */ if (iphdrlen + ipdatalen != recvlen) { logit(LOG_WARNING, 0, "Received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen); return; } igmp = (struct igmp *)(igmp_recv_buf + iphdrlen); group = igmp->igmp_group.s_addr; igmpdatalen = ipdatalen - IGMP_MINLEN; if (igmpdatalen < 0) { logit(LOG_WARNING, 0, "Received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inet_fmt(src, s1, sizeof(s1))); return; } IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Received %s from %s to %s", packet_kind(IPPROTO_IGMP, igmp->igmp_type, igmp->igmp_code), inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); switch (igmp->igmp_type) { case IGMP_MEMBERSHIP_QUERY: /* RFC 3376:7.1 */ if (ipdatalen == 8) { if (igmp->igmp_code == 0) igmp_version = 1; else igmp_version = 2; } else if (ipdatalen >= 12) { igmp_version = 3; } else { logit(LOG_DEBUG, 0, "Received invalid IGMP Membership query: Max Resp Code = %d, length = %d", igmp->igmp_code, ipdatalen); } accept_membership_query(src, dst, group, igmp->igmp_code, igmp_version); return; case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: accept_group_report(src, dst, group, igmp->igmp_type); return; case IGMP_V2_LEAVE_GROUP: accept_leave_message(src, dst, group); return; case IGMP_V3_MEMBERSHIP_REPORT: if (igmpdatalen < IGMP_V3_GROUP_RECORD_MIN_SIZE) { logit(LOG_DEBUG, 0, "Too short IGMP v3 Membership report: igmpdatalen(%d) < MIN(%d)", igmpdatalen, IGMP_V3_GROUP_RECORD_MIN_SIZE); return; } accept_membership_report(src, dst, (struct igmpv3_report *)(igmp_recv_buf + iphdrlen), recvlen - iphdrlen); return; case IGMP_DVMRP: /* XXX: TODO: most of the stuff below is not implemented. We are still * only PIM router. */ group = ntohl(group); switch (igmp->igmp_code) { case DVMRP_PROBE: dvmrp_accept_probe(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group); return; case DVMRP_REPORT: dvmrp_accept_report(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group); return; case DVMRP_ASK_NEIGHBORS: accept_neighbor_request(src, dst); return; case DVMRP_ASK_NEIGHBORS2: accept_neighbor_request2(src, dst); return; case DVMRP_NEIGHBORS: dvmrp_accept_neighbors(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group); return; case DVMRP_NEIGHBORS2: dvmrp_accept_neighbors2(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group); return; case DVMRP_PRUNE: dvmrp_accept_prune(src, dst, (uint8_t *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT: dvmrp_accept_graft(src, dst, (uint8_t *)(igmp+1), igmpdatalen); return; case DVMRP_GRAFT_ACK: dvmrp_accept_g_ack(src, dst, (uint8_t *)(igmp+1), igmpdatalen); return; case DVMRP_INFO_REQUEST: dvmrp_accept_info_request(src, dst, (uint8_t *)(igmp+1), igmpdatalen); return; case DVMRP_INFO_REPLY: dvmrp_accept_info_reply(src, dst, (uint8_t *)(igmp+1), igmpdatalen); return; default: logit(LOG_INFO, 0, "Ignoring unknown DVMRP message code %u from %s to %s", igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return; } case IGMP_PIM: return; /* TODO: this is PIM v1 message. Handle it?. */ case IGMP_MTRACE_RESP: return; /* TODO: implement it */ case IGMP_MTRACE: accept_mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code, igmpdatalen); return; default: logit(LOG_INFO, 0, "Ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return; } } static void send_ip_frame(uint32_t src, uint32_t dst, int type, int code, char *buf, size_t len) { int setloop = 0; struct ip *ip; struct sockaddr_in sin; char source[20], dest[20]; /* Prepare the IP header */ len += IP_IGMP_HEADER_LEN; ip = (struct ip *)buf; ip->ip_id = 0; /* let kernel fill in */ ip->ip_off = 0; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; #ifdef HAVE_IP_HDRINCL_BSD_ORDER ip->ip_len = len; #else ip->ip_len = htons(len); #endif if (IN_MULTICAST(ntohl(dst))) { k_set_if(igmp_socket, src); if (type != IGMP_DVMRP || dst == allhosts_group) { setloop = 1; k_set_loop(igmp_socket, TRUE); } #ifdef RAW_OUTPUT_IS_RAW ip->ip_ttl = curttl; } else { ip->ip_ttl = MAXTTL; #endif } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = dst; #ifdef HAVE_SA_LEN sin.sin_len = sizeof(sin); #endif IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Send %s from %s to %s", packet_kind(IPPROTO_IGMP, type, code), src == INADDR_ANY_N ? "INADDR_ANY" : inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); while (sendto(igmp_socket, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ if (errno == ENETDOWN || errno == ENODEV) check_vif_state(); else if (errno == EPERM || errno == EHOSTUNREACH) logit(LOG_WARNING, 0, "Not allowed to send IGMP message from %s to %s, possibly firewall" #ifdef __linux__ ", or SELinux policy violation," #endif " related problem." , inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); else logit(log_level(IPPROTO_IGMP, type, code), errno, "Sendto to %s on %s", inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2))); if (setloop) k_set_loop(igmp_socket, FALSE); return; } if (setloop) k_set_loop(igmp_socket, FALSE); IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_IGMP, type, code)) { logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", len, packet_kind(IPPROTO_IGMP, type, code), src == INADDR_ANY_N ? "INADDR_ANY" : inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } } /* * RFC-3376 states that Max Resp Code (MRC) and Querier's Query Interval Code * (QQIC) should be presented in floating point value if their value exceeds * 128. The following formula is used by IGMPv3 clients to calculate the * actual value of the floating point: * * 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ * |1| exp | mant | * +-+-+-+-+-+-+-+-+ * * QQI / MRT = (mant | 0x10) << (exp + 3) * * This requires us to find the largest set (fls) bit in the 15-bit number * and set the exponent based on its index in the bits 15-8. ie. * * exponent 0: igmp_fls(0000 0000 1000 0010) * exponent 5: igmp_fls(0001 0000 0000 0000) * exponent 7: igmp_fls(0111 0101 0000 0000) * * and set that as the exponent. The mantissa is set to the last 4 bits * remaining after the (3 + exponent) shifts to the right. * * Note! * The numbers 31744-32767 are the maximum we can present with floating * point that has an exponent of 3 and a mantissa of 4. After this the * implementation just wraps around back to zero. */ static inline uint8_t igmp_floating_point(unsigned int mantissa) { unsigned int exponent; /* Wrap around numbers larger than 2^15, since those can not be * presented with 7-bit floating point. */ mantissa &= 0x00007FFF; /* If top 8 bits are zero. */ if (!(mantissa & 0x00007F80)) return mantissa; /* Shift the mantissa and mark this code floating point. */ mantissa >>= 3; /* At this point the actual exponent (bits 7-5) are still 0, but the * exponent might be incremented below. */ exponent = 0x00000080; /* If bits 7-4 are not zero. */ if (mantissa & 0x00000F00) { mantissa >>= 4; /* The index of largest set bit is at least 4. */ exponent |= 0x00000040; } /* If bits 7-6 OR bits 3-2 are not zero. */ if (mantissa & 0x000000C0) { mantissa >>= 2; /* The index of largest set bit is atleast 6 if we shifted the * mantissa earlier or atleast 2 if we did not shift it. */ exponent |= 0x00000020; } /* If bit 7 OR bit 3 OR bit 1 is not zero. */ if (mantissa & 0x00000020) { mantissa >>= 1; /* The index of largest set bit is atleast 7 if we shifted the * mantissa two times earlier or atleast 3 if we shifted the * mantissa last time or atleast 1 if we did not shift it. */ exponent |= 0x00000010; } return exponent | (mantissa & 0x0000000F); } void send_igmp(char *buf, uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { size_t len = IGMP_MINLEN + datalen; struct igmpv3_query *igmp; igmp = (struct igmpv3_query *)(buf + IP_IGMP_HEADER_LEN); igmp->type = type; if (datalen >= 4) igmp->code = igmp_floating_point(code); else igmp->code = code; igmp->group = group; igmp->csum = 0; igmp->csum = inet_cksum((uint16_t *)igmp, len); if (datalen >= 4) { igmp->qrv = 2; igmp->qqic = igmp_floating_point(igmp_query_interval); } send_ip_frame(src, dst, type, code, buf, len); } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/igmp_proto.c000066400000000000000000000645171267035112600150120ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: igmp_proto.c,v 1.15 2001/09/10 20:31:36 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" typedef struct { vifi_t vifi; struct listaddr *g; uint32_t source; /* Source for SSM */ int q_time; /* IGMP Code */ int q_len; /* Data length */ } cbk_t; /* * Forward declarations. */ static void DelVif (void *arg); static int SetTimer (vifi_t vifi, struct listaddr *g, uint32_t source); static int SetVersionTimer (vifi_t vifi, struct listaddr *g); static int DeleteTimer (int id); static void send_query (struct uvif *v, uint32_t group, int interval); static void SendQuery (void *arg); static int SetQueryTimer (struct listaddr *g, vifi_t vifi, int to_expire, int q_time, int q_len); static uint32_t igmp_group_membership_timeout(void); /* The querier timeout depends on the configured query interval */ uint32_t igmp_query_interval = IGMP_QUERY_INTERVAL; uint32_t igmp_querier_timeout = IGMP_OTHER_QUERIER_PRESENT_INTERVAL; /* * Send group membership queries on that interface if I am querier. */ void query_groups(struct uvif *v) { int datalen = 4; int code = IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE; struct listaddr *g; v->uv_gq_timer = igmp_query_interval; if (v->uv_flags & VIFF_QUERIER) { /* IGMP version to use depends on the compatibility mode of the interface */ if (v->uv_flags & VIFF_IGMPV2) { /* RFC 3376: When in IGMPv2 mode, routers MUST send Periodic Queries truncated at the Group Address field (i.e., 8 bytes long) */ datalen = 0; } else if (v->uv_flags & VIFF_IGMPV1) { /* RFC 3376: When in IGMPv1 mode, routers MUST send Periodic Queries with a Max Response Time of 0 */ datalen = 0; code = 0; } IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Sending IGMP v%s query on %s", __func__, datalen == 4 ? "3" : "2", v->uv_name); send_igmp(igmp_send_buf, v->uv_lcl_addr, allhosts_group, IGMP_MEMBERSHIP_QUERY, code, 0, datalen); } /* * Decrement the old-hosts-present timer for each * active group on that vif. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (g->al_old > TIMER_INTERVAL) g->al_old -= TIMER_INTERVAL; else g->al_old = 0; } } /* * Process an incoming host membership query */ void accept_membership_query(uint32_t src, uint32_t dst __attribute__((unused)), uint32_t group, int tmo, int igmp_version) { vifi_t vifi; struct uvif *v; /* Ignore my own membership query */ if (local_address(src) != NO_VIF) return; /* Only v3 is allowed for SSM * TODO: Rate-limit messages? */ if (igmp_version != 3 && IN_PIM_SSM_RANGE(group)) { logit(LOG_WARNING, 0, "SSM addresses are not allowed in v%d query.", igmp_version); return; } /* TODO: modify for DVMRP?? */ if ((vifi = find_vif_direct(src)) == NO_VIF) { IF_DEBUG(DEBUG_IGMP) logit(LOG_INFO, 0, "Ignoring group membership query from non-adjacent host %s", inet_fmt(src, s1, sizeof(s1))); return; } v = &uvifs[vifi]; /* Do not accept messages of higher version than current * compatibility mode as specified in RFC 3376 - 7.3.1 */ if (v->uv_querier) { if ((igmp_version == 3 && (v->uv_flags & VIFF_IGMPV2)) || (igmp_version == 2 && (v->uv_flags & VIFF_IGMPV1))) { int i; /* * Exponentially back-off warning rate */ i = ++v->uv_igmpv1_warn; while (i && !(i & 1)) { i >>= 1; if (i == 1) { logit(LOG_WARNING, 0, "Received IGMP v%d query from %s on vif %d," " but I am configured for IGMP v%d network compatibility mode", igmp_version, inet_fmt(src, s1, sizeof(s1)), vifi, v->uv_flags & VIFF_IGMPV1 ? 1 : 2); } return; } } } if (!v->uv_querier || v->uv_querier->al_addr != src) { /* * This might be: * - A query from a new querier, with a lower source address * than the current querier (who might be me) * - A query from a new router that just started up and doesn't * know who the querier is. * - A query from the current querier */ if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr) : ntohl(v->uv_lcl_addr))) { IF_DEBUG(DEBUG_IGMP) { logit(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d", inet_fmt(src, s1, sizeof(s1)), v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2, sizeof(s2)) : "me", vifi); } if (!v->uv_querier) { v->uv_querier = (struct listaddr *) calloc(1, sizeof(struct listaddr)); if (!v->uv_querier) { logit(LOG_ERR, 0, "Failed calloc() in accept_membership_query()"); return; } v->uv_querier->al_next = (struct listaddr *)NULL; v->uv_querier->al_timer = 0; v->uv_querier->al_genid = 0; v->uv_querier->al_mv = 0; v->uv_querier->al_old = 0; v->uv_querier->al_index = 0; v->uv_querier->al_timerid = 0; v->uv_querier->al_query = 0; v->uv_querier->al_flags = 0; v->uv_flags &= ~VIFF_QUERIER; } v->uv_querier->al_addr = src; time(&v->uv_querier->al_ctime); } } /* * Reset the timer since we've received a query. */ if (v->uv_querier && src == v->uv_querier->al_addr) v->uv_querier->al_timer = 0; /* * If this is a Group-Specific query which we did not source, * we must set our membership timer to [Last Member Query Count] * * the [Max Response Time] in the packet. */ if (!(v->uv_flags & VIFF_IGMPV1) && group != 0 && src != v->uv_lcl_addr) { struct listaddr *g; IF_DEBUG(DEBUG_IGMP) { logit(LOG_DEBUG, 0, "Group-specific membership query for %s from %s on vif %d, timer %d", inet_fmt(group, s2, sizeof(s2)), inet_fmt(src, s1, sizeof(s1)), vifi, tmo); } for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr && g->al_query == 0) { /* setup a timeout to remove the group membership */ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT * tmo / IGMP_TIMER_SCALE; /* use al_query to record our presence in last-member state */ g->al_query = -1; g->al_timerid = SetTimer(vifi, g, 0); IF_DEBUG(DEBUG_IGMP) { logit(LOG_DEBUG, 0, "Timer for grp %s on vif %d set to %ld", inet_fmt(group, s2, sizeof(s2)), vifi, g->al_timer); } break; } } } } /* * Process an incoming group membership report. */ void accept_group_report(uint32_t igmp_src, uint32_t ssm_src, uint32_t group, int igmp_report_type) { vifi_t vifi; struct uvif *v; struct listaddr *g; struct listaddr *s = NULL; if ((vifi = find_vif_direct_local(igmp_src)) == NO_VIF) { IF_DEBUG(DEBUG_IGMP) { logit(LOG_INFO, 0, "Ignoring group membership report from non-adjacent host %s", inet_fmt(igmp_src, s1, sizeof(s1))); } return; } inet_fmt(igmp_src, s1, sizeof(s1)); inet_fmt(ssm_src, s2, sizeof(s2)); inet_fmt(group, s3, sizeof(s3)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): igmp_src %s ssm_src %s group %s report_type %i", __func__, s1, s2, s3, igmp_report_type); v = &uvifs[vifi]; /* * Look for the group in our group list; if found, reset its timer. */ for (g = v->uv_groups; g != NULL; g = g->al_next) { if (group == g->al_addr) { if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT) { g->al_old = DVMRP_OLD_AGE_THRESHOLD; if (!IN_PIM_SSM_RANGE(group) && g->al_pv>1) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v1 for group %s", s3); g->al_pv = 1; } } else if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG,0, "%s(): al_pv=%d", __func__, g->al_pv); if (g->al_pv > 2) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v2 for group %s", s3); g->al_pv = 2; } } g->al_reporter = igmp_src; /** delete old timers, set a timer for expiration **/ g->al_timer = igmp_group_membership_timeout(); if (g->al_query) g->al_query = DeleteTimer(g->al_query); if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); g->al_timerid = SetTimer(vifi, g, ssm_src); /* Reset timer for switching version back every time an older version report is received */ if (!IN_PIM_SSM_RANGE(group) && g->al_pv<3 && (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT || igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT)) { if (g->al_versiontimer) g->al_versiontimer = DeleteTimer(g->al_versiontimer); g->al_versiontimer = SetVersionTimer(vifi, g); } /* Find source */ if (IN_PIM_SSM_RANGE(group)) { for (s = g->al_sources; s; s = s->al_next) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Seek source %s, curr=%s", __func__, inet_fmt(ssm_src, s1, sizeof(s1)), inet_fmt(s->al_addr, s2, sizeof(s2))); if (ssm_src == s->al_addr) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Source found", __func__); break; } } if (!s) { /* Add new source */ s = (struct listaddr *)calloc(1, sizeof(struct listaddr)); if (!s) { logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__); return; } s->al_addr = ssm_src; s->al_next = g->al_sources; g->al_sources = s; IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Source %s added to g:%p", __func__, s2, g); } } /* TODO: might need to add a check if I am the forwarder??? */ /* if (v->uv_flags & VIFF_DR) */ if (IN_PIM_SSM_RANGE(group)) { IF_DEBUG(DEBUG_IGMP) logit(LOG_INFO, 0, "Add leaf (%s,%s)", s1, s3); add_leaf(vifi, ssm_src, group); } else { add_leaf(vifi, INADDR_ANY_N, group); } break; } } /* * If not found, add it to the list and update kernel cache. */ if (!g) { g = (struct listaddr *)calloc(1, sizeof(struct listaddr)); if (!g) { logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__); return; } g->al_addr = group; if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT) { g->al_old = DVMRP_OLD_AGE_THRESHOLD; IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v1 for group %s", s3); g->al_pv = 1; } else if (!IN_PIM_SSM_RANGE(group) && igmp_report_type == IGMP_V2_MEMBERSHIP_REPORT) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Change IGMP compatibility mode to v2 for group %s", s3); g->al_pv = 2; } else { g->al_pv = 3; } /* Add new source */ if (IN_PIM_SSM_RANGE(group)) { s = (struct listaddr *)calloc(1, sizeof(struct listaddr)); if (!s) { logit(LOG_ERR, errno, "%s(): Ran out of memory", __func__); return; } s->al_addr = ssm_src; s->al_next = g->al_sources; g->al_sources = s; IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Source %s added to new g:%p", __func__, s2, g); } /** set a timer for expiration **/ g->al_query = 0; g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL; g->al_reporter = igmp_src; g->al_timerid = SetTimer(vifi, g, ssm_src); /* Set timer for swithing version back if an older version report is received */ if (!IN_PIM_SSM_RANGE(group) && g->al_pv<3) { g->al_versiontimer = SetVersionTimer(vifi, g); } g->al_next = v->uv_groups; v->uv_groups = g; time(&g->al_ctime); /* TODO: might need to add a check if I am the forwarder??? */ /* if (v->uv_flags & VIFF_DR) */ if (IN_PIM_SSM_RANGE(group)) { IF_DEBUG(DEBUG_IGMP) logit(LOG_INFO, 0, "SSM group order from %s (%s,%s)", s1, s2, s3); add_leaf(vifi, ssm_src, group); } else { IF_DEBUG(DEBUG_IGMP) logit(LOG_INFO, 0, "SM group order from %s (*,%s)", s1, s3); add_leaf(vifi, INADDR_ANY_N, group); } } } /* TODO: send PIM prune message if the last member? */ void accept_leave_message(uint32_t src, uint32_t dst __attribute__((unused)), uint32_t group) { vifi_t vifi; struct uvif *v; struct listaddr *g; int datalen = 4; int code = IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE; /* TODO: modify for DVMRP ??? */ if ((vifi = find_vif_direct_local(src)) == NO_VIF) { IF_DEBUG(DEBUG_IGMP) logit(LOG_INFO, 0, "ignoring group leave report from non-adjacent host %s", inet_fmt(src, s1, sizeof(s1))); return; } inet_fmt(src, s1, sizeof(s1)); inet_fmt(dst, s2, sizeof(s2)); inet_fmt(group, s3, sizeof(s3)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): src %s dst %s group %s", __func__, s1, s2, s3); v = &uvifs[vifi]; #if 0 /* XXX: a PIM-SM last-hop router needs to know when a local member * has left. */ if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)) || (v->uv_flags & VIFF_IGMPV1)) return; #endif /* * Look for the group in our group list in order to set up a short-timeout * query. */ for (g = v->uv_groups; g; g = g->al_next) { if (group == g->al_addr) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "accept_leave_message(): old=%d query=%d", g->al_old, g->al_query); /* Ignore the leave message if there are old hosts present */ if (g->al_old) return; /* still waiting for a reply to a query, ignore the leave */ if (g->al_query) return; /* TODO: Remove the source. Ignore the leave if there are still sources left if (IN_PIM_SSM_RANGE(g->al_addr)) { for (s = g->al_sources; s != NULL; s = s->al_next) { if (dst == s->al_addr) { } } } */ /** delete old timer set a timer for expiration **/ if (g->al_timerid) g->al_timerid = DeleteTimer(g->al_timerid); #if IGMP_LAST_MEMBER_QUERY_COUNT != 2 /* This code needs to be updated to keep a counter of the number of queries remaining. */ #endif if (v->uv_flags & VIFF_QUERIER) { /* Use lowest IGMP version */ if (v->uv_flags & VIFF_IGMPV2 || g->al_pv <= 2) { datalen = 0; } else if (v->uv_flags & VIFF_IGMPV1 || g->al_pv == 1) { datalen = 0; code = 0; } IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): Sending IGMP v%s query (al_pv=%d)", __func__, datalen == 4 ? "3" : "2", g->al_pv); send_igmp(igmp_send_buf, v->uv_lcl_addr, g->al_addr, IGMP_MEMBERSHIP_QUERY, code, g->al_addr, datalen); } g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL * (IGMP_LAST_MEMBER_QUERY_COUNT + 1); g->al_query = SetQueryTimer(g, vifi, IGMP_LAST_MEMBER_QUERY_INTERVAL, code, datalen); g->al_timerid = SetTimer(vifi, g, dst); break; } } } /* * Time out old version compatibility mode */ static void SwitchVersion(void *arg) { cbk_t *cbk = (cbk_t *)arg; if (cbk->g->al_pv < 3) cbk->g->al_pv += 1; logit(LOG_INFO, 0, "Switch IGMP compatibility mode back to v%d for group %s", cbk->g->al_pv, inet_fmt(cbk->g->al_addr, s1, sizeof(s1))); } /* * Loop through and process all sources in a v3 record. * * Parameters: * igmp_report_type Report type of IGMP message * igmp_src Src address of IGMP message * group Multicast group * sources Pointer to the beginning of sources list in the IGMP message * report_pastend Pointer to the end of IGMP message * * Returns: * 1 if succeeded, 0 if failed */ int accept_sources(int igmp_report_type, uint32_t igmp_src, uint32_t group, uint8_t *sources, uint8_t *report_pastend, int rec_num_sources) { int j; uint8_t *src; char src_str[200]; for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { if ((src + 4) > report_pastend) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "src +4 > report_pastend"); return 0; } inet_ntop(AF_INET, src, src_str , sizeof(src_str)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Add source (%s,%s)", src_str, inet_fmt(group, s1, sizeof(s1))); accept_group_report(igmp_src, ((struct in_addr*)src)->s_addr, group, igmp_report_type); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Accepted, switch SPT (%s,%s)", src_str, inet_fmt(group, s1, sizeof(s1))); switch_shortest_path(((struct in_addr*)src)->s_addr, group); } return 1; } /* * Handle IGMP v3 membership reports (join/leave) */ void accept_membership_report(uint32_t src, uint32_t dst, struct igmpv3_report *report, ssize_t reportlen) { struct igmpv3_grec *record; int num_groups, i; uint8_t *report_pastend = (uint8_t *)report + reportlen; num_groups = ntohs(report->ngrec); if (num_groups < 0) { logit(LOG_INFO, 0, "Invalid Membership Report from %s: num_groups = %d", inet_fmt(src, s1, sizeof(s1)), num_groups); return; } IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "%s(): IGMP v3 report, %d bytes, from %s to %s with %d group records.", __func__, reportlen, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)), num_groups); record = &report->grec[0]; for (i = 0; i < num_groups; i++) { struct in_addr rec_group; uint8_t *sources; int rec_type; int rec_auxdatalen; int rec_num_sources; int j; char src_str[200]; int record_size = 0; rec_num_sources = ntohs(record->grec_nsrcs); rec_auxdatalen = record->grec_auxwords; record_size = sizeof(struct igmpv3_grec) + sizeof(uint32_t) * rec_num_sources + rec_auxdatalen; if ((uint8_t *)record + record_size > report_pastend) { logit(LOG_INFO, 0, "Invalid group report %p > %p", (uint8_t *)record + record_size, report_pastend); return; } rec_type = record->grec_type; rec_group.s_addr = (in_addr_t)record->grec_mca; sources = (u_int8_t *)record->grec_src; switch (rec_type) { case IGMP_MODE_IS_EXCLUDE: /* RFC 4604: A router SHOULD ignore a group record of type MODE_IS_EXCLUDE if it refers to an SSM destination address */ if (!IN_PIM_SSM_RANGE(rec_group.s_addr)) { if (rec_num_sources==0) { /* RFC 5790: EXCLUDE (*,G) join can be interpreted by the router as a request to include all sources. */ accept_group_report(src, 0 /*dst*/, rec_group.s_addr, report->type); } else { /* RFC 5790: LW-IGMPv3 does not use EXCLUDE filter-mode with a non-null source address list.*/ logit(LOG_INFO, 0, "Record type MODE_IS_EXCLUDE with non-null source list is currently unsupported."); } } break; case IGMP_CHANGE_TO_EXCLUDE_MODE: /* RFC 4604: A router SHOULD ignore a group record of type CHANGE_TO_EXCLUDE_MODE if it refers to an SSM destination address */ if (!IN_PIM_SSM_RANGE(rec_group.s_addr)) { if (rec_num_sources==0) { /* RFC 5790: EXCLUDE (*,G) join can be interpreted by the router as a request to include all sources. */ accept_group_report(src, 0 /*dst*/, rec_group.s_addr, report->type); } else { /* RFC 5790: LW-IGMPv3 does not use EXCLUDE filter-mode with a non-null source address list.*/ logit(LOG_DEBUG, 0, "Record type MODE_TO_EXCLUDE with non-null source list is currently unsupported."); } } break; case IGMP_MODE_IS_INCLUDE: if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Accept sources failed."); return; } break; case IGMP_CHANGE_TO_INCLUDE_MODE: if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Accept sources failed."); return; } break; case IGMP_ALLOW_NEW_SOURCES: if (!accept_sources(report->type, src, rec_group.s_addr, sources, report_pastend, rec_num_sources)) { logit(LOG_DEBUG, 0, "Accept sources failed."); return; } break; case IGMP_BLOCK_OLD_SOURCES: for (j = 0; j < rec_num_sources; j++) { uint32_t *gsrc = (uint32_t *)&record->grec_src[j]; if ((uint8_t *)gsrc > report_pastend) { logit(LOG_INFO, 0, "Invalid group record"); return; } inet_ntop(AF_INET, gsrc, src_str , sizeof(src_str)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Remove source[%d] (%s,%s)", j, src_str, inet_ntoa(rec_group)); accept_leave_message(src, *gsrc, rec_group.s_addr); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Accepted"); } break; default: // RFC3376: Unrecognized Record Type values MUST be silently ignored. break; } record = (struct igmpv3_grec *)((uint8_t *)record + record_size); } } /* * Calculate group membership timeout */ static uint32_t igmp_group_membership_timeout(void) { return IGMP_ROBUSTNESS_VARIABLE * igmp_query_interval + IGMP_QUERY_RESPONSE_INTERVAL; } /* * Time out record of a group membership on a vif */ static void DelVif(void *arg) { cbk_t *cbk = (cbk_t *)arg; vifi_t vifi = cbk->vifi; struct uvif *v = &uvifs[vifi]; struct listaddr *a, **anp, *g = cbk->g; struct listaddr *curr, *prev = NULL; if (IN_PIM_SSM_RANGE(g->al_addr)) { for (curr = g->al_sources; curr; prev = curr, curr = curr->al_next) { inet_fmt(cbk->source, s1, sizeof(s1)); inet_fmt(curr->al_addr, s2, sizeof(s2)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "DelVif: Seek source %s, curr=%s (%p)", s1, s2, curr); if (curr->al_addr == cbk->source) { if (!prev) g->al_sources = curr->al_next; /* Remove from beginning */ else prev->al_next = curr->al_next; free(curr); break; } } IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "DelVif: %s sources left", g->al_sources ? "Still" : "No"); if (g->al_sources) { IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "DelVif: Not last source, g->al_sources --> %s", inet_fmt(g->al_sources->al_addr, s1, sizeof(s1))); delete_leaf(vifi, cbk->source, g->al_addr); free(cbk); return; /* This was not last source for this interface */ } } /* * Group has expired * delete all kernel cache entries with this group */ if (g->al_query) DeleteTimer(g->al_query); if (g->al_versiontimer) DeleteTimer(g->al_versiontimer); if (IN_PIM_SSM_RANGE(g->al_addr)) { inet_fmt(g->al_addr, s1, sizeof(s1)); inet_fmt(cbk->source, s2, sizeof(s2)); IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "SSM range, source specific delete"); /* delete (S,G) entry */ IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "DelVif: vif:%d(%s), (S=%s,G=%s)", vifi, v->uv_name, s2, s1); delete_leaf(vifi, cbk->source, g->al_addr); } else { delete_leaf(vifi, INADDR_ANY_N, g->al_addr); } anp = &(v->uv_groups); while ((a = *anp)) { if (a == g) { *anp = a->al_next; free(a->al_sources); free(a); } else { anp = &a->al_next; } } free(cbk); } /* * Set a timer to switch version back on a vif. */ static int SetVersionTimer(vifi_t vifi, struct listaddr *g) { cbk_t *cbk; cbk = (cbk_t *)calloc(1, sizeof(cbk_t)); if (!cbk) { logit(LOG_ERR, 0, "Failed calloc() in SetVersionTimer()\n"); return -1; } cbk->vifi = vifi; cbk->g = g; return timer_setTimer(IGMP_ROBUSTNESS_VARIABLE * igmp_query_interval + IGMP_QUERY_RESPONSE_INTERVAL, SwitchVersion, cbk); } /* * Set a timer to delete the record of a group membership on a vif. */ static int SetTimer(vifi_t vifi, struct listaddr *g, uint32_t source) { cbk_t *cbk; cbk = (cbk_t *) calloc(1, sizeof(cbk_t)); if (!cbk) { logit(LOG_ERR, 0, "Failed calloc() in SetTimer()"); return -1; } cbk->vifi = vifi; cbk->g = g; cbk->source = source; IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Set delete timer for group: %s", inet_ntoa(*((struct in_addr *)&g->al_addr))); return timer_setTimer(g->al_timer, DelVif, cbk); } /* * Delete a timer that was set above. */ static int DeleteTimer(int id) { timer_clearTimer(id); return 0; } /* * Send IGMP Query */ static void send_query(struct uvif *v, uint32_t group, int interval) { if (v->uv_flags & VIFF_QUERIER) { send_igmp(igmp_send_buf, v->uv_lcl_addr, group, IGMP_MEMBERSHIP_QUERY, interval, group != allhosts_group ? group : 0, 0); } } /* * Send a group-specific query. */ static void SendQuery(void *arg) { cbk_t *cbk = (cbk_t *)arg; IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "SendQuery: Send IGMP v%s query", cbk->q_len == 4 ? "3" : "2"); send_query(&uvifs[cbk->vifi], cbk->g->al_addr, cbk->q_time); cbk->g->al_query = 0; free(cbk); } /* * Set a timer to send a group-specific query. */ static int SetQueryTimer(struct listaddr *g, vifi_t vifi, int to_expire, int q_time, int q_len) { cbk_t *cbk; cbk = (cbk_t *)calloc(1, sizeof(cbk_t)); if (!cbk) { logit(LOG_ERR, 0, "Failed calloc() in SetQueryTimer()"); return -1; } cbk->g = g; cbk->q_time = q_time; cbk->q_len = q_len; cbk->vifi = vifi; return timer_setTimer(to_expire, SendQuery, cbk); } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/igmpv2.h000066400000000000000000000021111267035112600140220ustar00rootroot00000000000000/* * The mrouted program is covered by the license in the accompanying file * named "LICENSE". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * igmpv2.h,v 3.8 1997/05/01 23:10:31 fenner Exp */ /* * Constants for IGMP Version 2. Several of these, especially the * robustness variable, should be variables and not constants. */ #define IGMP_ROBUSTNESS_VARIABLE 3 #define IGMP_QUERY_INTERVAL 12 #define IGMP_QUERY_RESPONSE_INTERVAL 10 #define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \ IGMP_QUERY_INTERVAL + \ IGMP_QUERY_RESPONSE_INTERVAL) #define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \ IGMP_QUERY_INTERVAL + \ IGMP_QUERY_RESPONSE_INTERVAL / 2) #define IGMP_STARTUP_QUERY_INTERVAL 30 #define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE #define IGMP_LAST_MEMBER_QUERY_INTERVAL 1 #define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE pimd-2.3.2/igmpv3.h000066400000000000000000000060171267035112600140340ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __PIMD_IGMPV3_H__ #define __PIMD_IGMPV3_H__ /* * IGMPv3 report modes. */ #ifndef IGMP_MODE_IS_INCLUDE #define IGMP_DO_NOTHING 0 /* don't send a record */ #define IGMP_MODE_IS_INCLUDE 1 /* MODE_IN */ #define IGMP_MODE_IS_EXCLUDE 2 /* MODE_EX */ #define IGMP_CHANGE_TO_INCLUDE_MODE 3 /* TO_IN */ #define IGMP_CHANGE_TO_EXCLUDE_MODE 4 /* TO_EX */ #define IGMP_ALLOW_NEW_SOURCES 5 /* ALLOW_NEW */ #define IGMP_BLOCK_OLD_SOURCES 6 /* BLOCK_OLD */ #endif struct igmpv3_query { uint8_t type; uint8_t code; uint16_t csum; uint32_t group; #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) uint8_t qrv:3, suppress:1, resv:4; #else uint8_t resv:4, suppress:1, qrv:3; #endif uint8_t qqic; uint16_t nsrcs; uint32_t srcs[0]; }; struct igmpv3_grec { uint8_t grec_type; uint8_t grec_auxwords; uint16_t grec_nsrcs; uint32_t grec_mca; uint32_t grec_src[0]; }; #define IGMP_GRPREC_HDRLEN 8 #define IGMP_V3_GROUP_RECORD_MIN_SIZE 8 struct igmpv3_report { uint8_t type; uint8_t resv1; uint16_t csum; uint16_t resv2; uint16_t ngrec; struct igmpv3_grec grec[0]; }; #ifndef IGMP_V3_REPORT_MINLEN #define IGMP_V3_REPORT_MINLEN 8 #define IGMP_V3_REPORT_MAXRECS 65535 #endif #endif /* __PIMD_IGMPV3_H__ */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/include/000077500000000000000000000000001267035112600140755ustar00rootroot00000000000000pimd-2.3.2/include/README000066400000000000000000000016121267035112600147550ustar00rootroot00000000000000This directory contains missing system headers. These files should only be used as a last ditch effort on systems that miss, e.g. netinet/ip_mroute.h, netinet/in.h or similar. Some effort have been made to see to different locations across systems in the file defs.h. In most cases, the use of these files is activated by uncommenting the matching section in config.mk, or adding to the DEFS and INCLUDES. linux/ - For *really* old kernels, possibly pre-2.0. Do NOT include for any recent systems, at all! Only pim.h missing, even on 2.6.32. openbsd/ - For older releases, not needed for releases after, at least, 2003. netbsd/ - For older releases, not needed for releases after, at least, 2003. freebsd/ - For older releases, not needed for releases after, at least, 2003. freebsd2/ - For *really* old FreeBSD 2.x releases. sunos-*/ - For *really* old SunOS/OSF1 based systems. pimd-2.3.2/include/freebsd/000077500000000000000000000000001267035112600155075ustar00rootroot00000000000000pimd-2.3.2/include/freebsd/netinet/000077500000000000000000000000001267035112600171555ustar00rootroot00000000000000pimd-2.3.2/include/freebsd/netinet/in.h000066400000000000000000000670301267035112600177420ustar00rootroot00000000000000/*- * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 * $FreeBSD: src/sys/netinet/in.h,v 1.116 2010/08/19 11:31:03 anchie Exp $ */ #ifndef _NETINET_IN_H_ #define _NETINET_IN_H_ #include #include #include /* Protocols common to RFC 1700, POSIX, and X/Open. */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define INADDR_ANY (u_int32_t)0x00000000 #define INADDR_BROADCAST (u_int32_t)0xffffffff /* must be masked */ #ifndef _UINT8_T_DECLARED typedef __uint8_t uint8_t; #define _UINT8_T_DECLARED #endif #ifndef _UINT16_T_DECLARED typedef __uint16_t uint16_t; #define _UINT16_T_DECLARED #endif #ifndef _UINT32_T_DECLARED typedef __uint32_t uint32_t; #define _UINT32_T_DECLARED #endif #ifndef _IN_ADDR_T_DECLARED typedef uint32_t in_addr_t; #define _IN_ADDR_T_DECLARED #endif #ifndef _IN_PORT_T_DECLARED typedef uint16_t in_port_t; #define _IN_PORT_T_DECLARED #endif #ifndef _SA_FAMILY_T_DECLARED typedef __sa_family_t sa_family_t; #define _SA_FAMILY_T_DECLARED #endif /* Internet address (a structure for historical reasons). */ #ifndef _STRUCT_IN_ADDR_DECLARED struct in_addr { in_addr_t s_addr; }; #define _STRUCT_IN_ADDR_DECLARED #endif #ifndef _SOCKLEN_T_DECLARED typedef __socklen_t socklen_t; #define _SOCKLEN_T_DECLARED #endif #include /* Socket address, internet style. */ struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; }; #if !defined(_KERNEL) && __BSD_VISIBLE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS uint32_t htonl(uint32_t); uint16_t htons(uint16_t); uint32_t ntohl(uint32_t); uint16_t ntohs(uint16_t); __END_DECLS #endif #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif #endif /* !_KERNEL && __BSD_VISIBLE */ #if __POSIX_VISIBLE >= 200112 #define IPPROTO_RAW 255 /* raw IP packet */ #define INET_ADDRSTRLEN 16 #endif #if __BSD_VISIBLE /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981, and numerous additions. */ /* * Protocols (RFC 1700) */ #define IPPROTO_HOPOPTS 0 /* IP6 hop-by-hop options */ #define IPPROTO_IGMP 2 /* group mgmt protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPV4 4 /* IPv4 encapsulation */ #define IPPROTO_IPIP IPPROTO_IPV4 /* for compatibility */ #define IPPROTO_ST 7 /* Stream protocol II */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PIGP 9 /* private interior gateway */ #define IPPROTO_RCCMON 10 /* BBN RCC Monitoring */ #define IPPROTO_NVPII 11 /* network voice protocol*/ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_ARGUS 13 /* Argus */ #define IPPROTO_EMCON 14 /* EMCON */ #define IPPROTO_XNET 15 /* Cross Net Debugger */ #define IPPROTO_CHAOS 16 /* Chaos*/ #define IPPROTO_MUX 18 /* Multiplexing */ #define IPPROTO_MEAS 19 /* DCN Measurement Subsystems */ #define IPPROTO_HMP 20 /* Host Monitoring */ #define IPPROTO_PRM 21 /* Packet Radio Measurement */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_TRUNK1 23 /* Trunk-1 */ #define IPPROTO_TRUNK2 24 /* Trunk-2 */ #define IPPROTO_LEAF1 25 /* Leaf-1 */ #define IPPROTO_LEAF2 26 /* Leaf-2 */ #define IPPROTO_RDP 27 /* Reliable Data */ #define IPPROTO_IRTP 28 /* Reliable Transaction */ #define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ #define IPPROTO_BLT 30 /* Bulk Data Transfer */ #define IPPROTO_NSP 31 /* Network Services */ #define IPPROTO_INP 32 /* Merit Internodal */ #define IPPROTO_SEP 33 /* Sequential Exchange */ #define IPPROTO_3PC 34 /* Third Party Connect */ #define IPPROTO_IDPR 35 /* InterDomain Policy Routing */ #define IPPROTO_XTP 36 /* XTP */ #define IPPROTO_DDP 37 /* Datagram Delivery */ #define IPPROTO_CMTP 38 /* Control Message Transport */ #define IPPROTO_TPXX 39 /* TP++ Transport */ #define IPPROTO_IL 40 /* IL transport protocol */ #define IPPROTO_IPV6 41 /* IP6 header */ #define IPPROTO_SDRP 42 /* Source Demand Routing */ #define IPPROTO_ROUTING 43 /* IP6 routing header */ #define IPPROTO_FRAGMENT 44 /* IP6 fragmentation header */ #define IPPROTO_IDRP 45 /* InterDomain Routing*/ #define IPPROTO_RSVP 46 /* resource reservation */ #define IPPROTO_GRE 47 /* General Routing Encap. */ #define IPPROTO_MHRP 48 /* Mobile Host Routing */ #define IPPROTO_BHA 49 /* BHA */ #define IPPROTO_ESP 50 /* IP6 Encap Sec. Payload */ #define IPPROTO_AH 51 /* IP6 Auth Header */ #define IPPROTO_INLSP 52 /* Integ. Net Layer Security */ #define IPPROTO_SWIPE 53 /* IP with encryption */ #define IPPROTO_NHRP 54 /* Next Hop Resolution */ #define IPPROTO_MOBILE 55 /* IP Mobility */ #define IPPROTO_TLSP 56 /* Transport Layer Security */ #define IPPROTO_SKIP 57 /* SKIP */ #define IPPROTO_ICMPV6 58 /* ICMP6 */ #define IPPROTO_NONE 59 /* IP6 no next header */ #define IPPROTO_DSTOPTS 60 /* IP6 destination option */ #define IPPROTO_AHIP 61 /* any host internal protocol */ #define IPPROTO_CFTP 62 /* CFTP */ #define IPPROTO_HELLO 63 /* "hello" routing protocol */ #define IPPROTO_SATEXPAK 64 /* SATNET/Backroom EXPAK */ #define IPPROTO_KRYPTOLAN 65 /* Kryptolan */ #define IPPROTO_RVD 66 /* Remote Virtual Disk */ #define IPPROTO_IPPC 67 /* Pluribus Packet Core */ #define IPPROTO_ADFS 68 /* Any distributed FS */ #define IPPROTO_SATMON 69 /* Satnet Monitoring */ #define IPPROTO_VISA 70 /* VISA Protocol */ #define IPPROTO_IPCV 71 /* Packet Core Utility */ #define IPPROTO_CPNX 72 /* Comp. Prot. Net. Executive */ #define IPPROTO_CPHB 73 /* Comp. Prot. HeartBeat */ #define IPPROTO_WSN 74 /* Wang Span Network */ #define IPPROTO_PVP 75 /* Packet Video Protocol */ #define IPPROTO_BRSATMON 76 /* BackRoom SATNET Monitoring */ #define IPPROTO_ND 77 /* Sun net disk proto (temp.) */ #define IPPROTO_WBMON 78 /* WIDEBAND Monitoring */ #define IPPROTO_WBEXPAK 79 /* WIDEBAND EXPAK */ #define IPPROTO_EON 80 /* ISO cnlp */ #define IPPROTO_VMTP 81 /* VMTP */ #define IPPROTO_SVMTP 82 /* Secure VMTP */ #define IPPROTO_VINES 83 /* Banyon VINES */ #define IPPROTO_TTP 84 /* TTP */ #define IPPROTO_IGP 85 /* NSFNET-IGP */ #define IPPROTO_DGP 86 /* dissimilar gateway prot. */ #define IPPROTO_TCF 87 /* TCF */ #define IPPROTO_IGRP 88 /* Cisco/GXS IGRP */ #define IPPROTO_OSPFIGP 89 /* OSPFIGP */ #define IPPROTO_SRPC 90 /* Strite RPC protocol */ #define IPPROTO_LARP 91 /* Locus Address Resoloution */ #define IPPROTO_MTP 92 /* Multicast Transport */ #define IPPROTO_AX25 93 /* AX.25 Frames */ #define IPPROTO_IPEIP 94 /* IP encapsulated in IP */ #define IPPROTO_MICP 95 /* Mobile Int.ing control */ #define IPPROTO_SCCSP 96 /* Semaphore Comm. security */ #define IPPROTO_ETHERIP 97 /* Ethernet IP encapsulation */ #define IPPROTO_ENCAP 98 /* encapsulation header */ #define IPPROTO_APES 99 /* any private encr. scheme */ #define IPPROTO_GMTP 100 /* GMTP*/ #define IPPROTO_IPCOMP 108 /* payload compression (IPComp) */ #define IPPROTO_SCTP 132 /* SCTP */ #define IPPROTO_MH 135 /* IPv6 Mobility Header */ /* 101-254: Partly Unassigned */ #define IPPROTO_PIM 103 /* Protocol Independent Mcast */ #define IPPROTO_CARP 112 /* CARP */ #define IPPROTO_PGM 113 /* PGM */ #define IPPROTO_PFSYNC 240 /* PFSYNC */ /* 255: Reserved */ /* BSD Private, local use, namespace incursion, no longer used */ #define IPPROTO_OLD_DIVERT 254 /* OLD divert pseudo-proto */ #define IPPROTO_MAX 256 /* last return value of *_input(), meaning "all job for this pkt is done". */ #define IPPROTO_DONE 257 /* Only used internally, so can be outside the range of valid IP protocols. */ #define IPPROTO_DIVERT 258 /* divert pseudo-protocol */ #define IPPROTO_SEND 259 /* SeND pseudo-protocol */ /* * Defined to avoid confusion. The master value is defined by * PROTO_SPACER in sys/protosw.h. */ #define IPPROTO_SPACER 32767 /* spacer for loadable protos */ /* * Local port number conventions: * * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * The default range is IPPORT_HIFIRSTAUTO through * IPPORT_HILASTAUTO, although that is settable by sysctl. * * A user may set the IPPROTO_IP option IP_PORTRANGE to change this * default assignment range. * * The value IP_PORTRANGE_DEFAULT causes the default behavior. * * The value IP_PORTRANGE_HIGH changes the range of candidate port numbers * into the "high" range. These are reserved for client outbound connections * which do not want to be filtered by any firewalls. * * The value IP_PORTRANGE_LOW changes the range to the "low" are * that is (by convention) restricted to privileged processes. This * convention is based on "vouchsafe" principles only. It is only secure * if you trust the remote host to restrict these ports. * * The default range of ports and the high range can be changed by * sysctl(3). (net.inet.ip.port{hi,low}{first,last}_auto) * * Changing those values has bad security implications if you are * using a stateless firewall that is allowing packets outside of that * range in order to allow transparent outgoing connections. * * Such a firewall configuration will generally depend on the use of these * default values. If you change them, you may find your Security * Administrator looking for you with a heavy object. * * For a slightly more orthodox text view on this: * * ftp://ftp.isi.edu/in-notes/iana/assignments/port-numbers * * port numbers are divided into three ranges: * * 0 - 1023 Well Known Ports * 1024 - 49151 Registered Ports * 49152 - 65535 Dynamic and/or Private Ports * */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). (IP_PORTRANGE_LOW) */ #define IPPORT_RESERVED 1024 /* * Default local port range, used by IP_PORTRANGE_DEFAULT */ #define IPPORT_EPHEMERALFIRST 10000 #define IPPORT_EPHEMERALLAST 65535 /* * Dynamic port range, used by IP_PORTRANGE_HIGH. */ #define IPPORT_HIFIRSTAUTO 49152 #define IPPORT_HILASTAUTO 65535 /* * Scanning for a free reserved port return a value below IPPORT_RESERVED, * but higher than IPPORT_RESERVEDSTART. Traditionally the start value was * 512, but that conflicts with some well-known-services that firewalls may * have a fit if we use. */ #define IPPORT_RESERVEDSTART 600 #define IPPORT_MAX 65535 /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((u_int32_t)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((u_int32_t)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((u_int32_t)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define IN_CLASSD(i) (((u_int32_t)(i) & 0xf0000000) == 0xe0000000) #define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ #define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ #define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) #define IN_BADCLASS(i) (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) #define IN_LINKLOCAL(i) (((u_int32_t)(i) & 0xffff0000) == 0xa9fe0000) #define IN_LOOPBACK(i) (((u_int32_t)(i) & 0xff000000) == 0x7f000000) #define IN_ZERONET(i) (((u_int32_t)(i) & 0xff000000) == 0) #define IN_PRIVATE(i) ((((u_int32_t)(i) & 0xff000000) == 0x0a000000) || \ (((u_int32_t)(i) & 0xfff00000) == 0xac100000) || \ (((u_int32_t)(i) & 0xffff0000) == 0xc0a80000)) #define IN_LOCAL_GROUP(i) (((u_int32_t)(i) & 0xffffff00) == 0xe0000000) #define IN_ANY_LOCAL(i) (IN_LINKLOCAL(i) || IN_LOCAL_GROUP(i)) #define INADDR_LOOPBACK (u_int32_t)0x7f000001 #ifndef _KERNEL #define INADDR_NONE 0xffffffff /* -1 return */ #endif #define INADDR_UNSPEC_GROUP (u_int32_t)0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP (u_int32_t)0xe0000001 /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP (u_int32_t)0xe0000002 /* 224.0.0.2 */ #define INADDR_ALLRPTS_GROUP (u_int32_t)0xe0000016 /* 224.0.0.22, IGMPv3 */ #define INADDR_CARP_GROUP (u_int32_t)0xe0000012 /* 224.0.0.18 */ #define INADDR_PFSYNC_GROUP (u_int32_t)0xe00000f0 /* 224.0.0.240 */ #define INADDR_ALLMDNS_GROUP (u_int32_t)0xe00000fb /* 224.0.0.251 */ #define INADDR_MAX_LOCAL_GROUP (u_int32_t)0xe00000ff /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Options for use with [gs]etsockopt at the IP level. * First word of comment is data type; bool is stored in int. */ #define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */ #define IP_HDRINCL 2 /* int; header is included with data */ #define IP_TOS 3 /* int; IP type of service and preced. */ #define IP_TTL 4 /* int; IP time to live */ #define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */ #define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */ #define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */ #define IP_SENDSRCADDR IP_RECVDSTADDR /* cmsg_type to set src addr */ #define IP_RETOPTS 8 /* ip_opts; set/get IP options */ #define IP_MULTICAST_IF 9 /* struct in_addr *or* struct ip_mreqn; * set/get IP multicast i/f */ #define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */ #define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */ #define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */ #define IP_MULTICAST_VIF 14 /* set/get IP mcast virt. iface */ #define IP_RSVP_ON 15 /* enable RSVP in kernel */ #define IP_RSVP_OFF 16 /* disable RSVP in kernel */ #define IP_RSVP_VIF_ON 17 /* set RSVP per-vif socket */ #define IP_RSVP_VIF_OFF 18 /* unset RSVP per-vif socket */ #define IP_PORTRANGE 19 /* int; range to choose for unspec port */ #define IP_RECVIF 20 /* bool; receive reception if w/dgram */ /* for IPSEC */ #define IP_IPSEC_POLICY 21 /* int; set/get security policy */ #define IP_FAITH 22 /* bool; accept FAITH'ed connections */ #define IP_ONESBCAST 23 /* bool: send all-ones broadcast */ #define IP_BINDANY 24 /* bool: allow bind to any address */ /* * Options for controlling the firewall and dummynet. * Historical options (from 40 to 64) will eventually be * replaced by only two options, IP_FW3 and IP_DUMMYNET3. */ #define IP_FW_TABLE_ADD 40 /* add entry */ #define IP_FW_TABLE_DEL 41 /* delete entry */ #define IP_FW_TABLE_FLUSH 42 /* flush table */ #define IP_FW_TABLE_GETSIZE 43 /* get table size */ #define IP_FW_TABLE_LIST 44 /* list table contents */ #define IP_FW3 48 /* generic ipfw v.3 sockopts */ #define IP_DUMMYNET3 49 /* generic dummynet v.3 sockopts */ #define IP_FW_ADD 50 /* add a firewall rule to chain */ #define IP_FW_DEL 51 /* delete a firewall rule from chain */ #define IP_FW_FLUSH 52 /* flush firewall rule chain */ #define IP_FW_ZERO 53 /* clear single/all firewall counter(s) */ #define IP_FW_GET 54 /* get entire firewall rule chain */ #define IP_FW_RESETLOG 55 /* reset logging counters */ #define IP_FW_NAT_CFG 56 /* add/config a nat rule */ #define IP_FW_NAT_DEL 57 /* delete a nat rule */ #define IP_FW_NAT_GET_CONFIG 58 /* get configuration of a nat rule */ #define IP_FW_NAT_GET_LOG 59 /* get log of a nat rule */ #define IP_DUMMYNET_CONFIGURE 60 /* add/configure a dummynet pipe */ #define IP_DUMMYNET_DEL 61 /* delete a dummynet pipe from chain */ #define IP_DUMMYNET_FLUSH 62 /* flush dummynet */ #define IP_DUMMYNET_GET 64 /* get entire dummynet pipes */ #define IP_RECVTTL 65 /* bool; receive IP TTL w/dgram */ #define IP_MINTTL 66 /* minimum TTL for packet or drop */ #define IP_DONTFRAG 67 /* don't fragment packet */ /* IPv4 Source Filter Multicast API [RFC3678] */ #define IP_ADD_SOURCE_MEMBERSHIP 70 /* join a source-specific group */ #define IP_DROP_SOURCE_MEMBERSHIP 71 /* drop a single source */ #define IP_BLOCK_SOURCE 72 /* block a source */ #define IP_UNBLOCK_SOURCE 73 /* unblock a source */ /* The following option is private; do not use it from user applications. */ #define IP_MSFILTER 74 /* set/get filter list */ /* Protocol Independent Multicast API [RFC3678] */ #define MCAST_JOIN_GROUP 80 /* join an any-source group */ #define MCAST_LEAVE_GROUP 81 /* leave all sources for group */ #define MCAST_JOIN_SOURCE_GROUP 82 /* join a source-specific group */ #define MCAST_LEAVE_SOURCE_GROUP 83 /* leave a single source */ #define MCAST_BLOCK_SOURCE 84 /* block a source */ #define MCAST_UNBLOCK_SOURCE 85 /* unblock a source */ /* * Defaults and limits for options */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ /* * The imo_membership vector for each socket is now dynamically allocated at * run-time, bounded by USHRT_MAX, and is reallocated when needed, sized * according to a power-of-two increment. */ #define IP_MIN_MEMBERSHIPS 31 #define IP_MAX_MEMBERSHIPS 4095 #define IP_MAX_SOURCE_FILTER 1024 /* XXX to be unused */ /* * Default resource limits for IPv4 multicast source filtering. * These may be modified by sysctl. */ #define IP_MAX_GROUP_SRC_FILTER 512 /* sources per group */ #define IP_MAX_SOCK_SRC_FILTER 128 /* sources per socket/group */ #define IP_MAX_SOCK_MUTE_FILTER 128 /* XXX no longer used */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Modified argument structure for IP_MULTICAST_IF, obtained from Linux. * This is used to specify an interface index for multicast sends, as * the IPv4 legacy APIs do not support this (unless IP_SENDIF is available). */ struct ip_mreqn { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_address; /* local IP address of interface */ int imr_ifindex; /* Interface index; cast to uint32_t */ }; /* * Argument structure for IPv4 Multicast Source Filter APIs. [RFC3678] */ struct ip_mreq_source { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_sourceaddr; /* IP address of source */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Argument structures for Protocol-Independent Multicast Source * Filter APIs. [RFC3678] */ struct group_req { uint32_t gr_interface; /* interface index */ struct sockaddr_storage gr_group; /* group address */ }; struct group_source_req { uint32_t gsr_interface; /* interface index */ struct sockaddr_storage gsr_group; /* group address */ struct sockaddr_storage gsr_source; /* source address */ }; #ifndef __MSFILTERREQ_DEFINED #define __MSFILTERREQ_DEFINED /* * The following structure is private; do not use it from user applications. * It is used to communicate IP_MSFILTER/IPV6_MSFILTER information between * the RFC 3678 libc functions and the kernel. */ struct __msfilterreq { uint32_t msfr_ifindex; /* interface index */ uint32_t msfr_fmode; /* filter mode for group */ uint32_t msfr_nsrcs; /* # of sources in msfr_srcs */ struct sockaddr_storage msfr_group; /* group address */ struct sockaddr_storage *msfr_srcs; /* pointer to the first member * of a contiguous array of * sources to filter in full. */ }; #endif struct sockaddr; /* * Advanced (Full-state) APIs [RFC3678] * The RFC specifies uint_t for the 6th argument to [sg]etsourcefilter(). * We use uint32_t here to be consistent. */ int setipv4sourcefilter(int, struct in_addr, struct in_addr, uint32_t, uint32_t, struct in_addr *); int getipv4sourcefilter(int, struct in_addr, struct in_addr, uint32_t *, uint32_t *, struct in_addr *); int setsourcefilter(int, uint32_t, struct sockaddr *, socklen_t, uint32_t, uint32_t, struct sockaddr_storage *); int getsourcefilter(int, uint32_t, struct sockaddr *, socklen_t, uint32_t *, uint32_t *, struct sockaddr_storage *); /* * Filter modes; also used to represent per-socket filter mode internally. */ #define MCAST_UNDEFINED 0 /* fmode: not yet defined */ #define MCAST_INCLUDE 1 /* fmode: include these source(s) */ #define MCAST_EXCLUDE 2 /* fmode: exclude these source(s) */ /* * Argument for IP_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IP_PORTRANGE_DEFAULT 0 /* default range */ #define IP_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IP_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ /* * Definitions for inet sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ #define IPPROTO_MAXID (IPPROTO_AH + 1) /* don't list to IPPROTO_MAX */ #define CTL_IPPROTO_NAMES { \ { "ip", CTLTYPE_NODE }, \ { "icmp", CTLTYPE_NODE }, \ { "igmp", CTLTYPE_NODE }, \ { "ggp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { "tcp", CTLTYPE_NODE }, \ { 0, 0 }, \ { "egp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pup", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "udp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "idp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "ipsec", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pim", CTLTYPE_NODE }, \ } /* * Names for IP sysctl objects */ #define IPCTL_FORWARDING 1 /* act as router */ #define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */ #define IPCTL_DEFTTL 3 /* default TTL */ #ifdef notyet #define IPCTL_DEFMTU 4 /* default MTU */ #endif #define IPCTL_RTEXPIRE 5 /* cloned route expiration time */ #define IPCTL_RTMINEXPIRE 6 /* min value for expiration time */ #define IPCTL_RTMAXCACHE 7 /* trigger level for dynamic expire */ #define IPCTL_SOURCEROUTE 8 /* may perform source routes */ #define IPCTL_DIRECTEDBROADCAST 9 /* may re-broadcast received packets */ #define IPCTL_INTRQMAXLEN 10 /* max length of netisr queue */ #define IPCTL_INTRQDROPS 11 /* number of netisr q drops */ #define IPCTL_STATS 12 /* ipstat structure */ #define IPCTL_ACCEPTSOURCEROUTE 13 /* may accept source routed packets */ #define IPCTL_FASTFORWARDING 14 /* use fast IP forwarding code */ #define IPCTL_KEEPFAITH 15 /* FAITH IPv4->IPv6 translater ctl */ #define IPCTL_GIF_TTL 16 /* default TTL for gif encap packet */ #define IPCTL_MAXID 17 #define IPCTL_NAMES { \ { 0, 0 }, \ { "forwarding", CTLTYPE_INT }, \ { "redirect", CTLTYPE_INT }, \ { "ttl", CTLTYPE_INT }, \ { "mtu", CTLTYPE_INT }, \ { "rtexpire", CTLTYPE_INT }, \ { "rtminexpire", CTLTYPE_INT }, \ { "rtmaxcache", CTLTYPE_INT }, \ { "sourceroute", CTLTYPE_INT }, \ { "directed-broadcast", CTLTYPE_INT }, \ { "intr-queue-maxlen", CTLTYPE_INT }, \ { "intr-queue-drops", CTLTYPE_INT }, \ { "stats", CTLTYPE_STRUCT }, \ { "accept_sourceroute", CTLTYPE_INT }, \ { "fastforwarding", CTLTYPE_INT }, \ } #endif /* __BSD_VISIBLE */ #ifdef _KERNEL struct ifnet; struct mbuf; /* forward declarations for Standard C */ int in_broadcast(struct in_addr, struct ifnet *); int in_canforward(struct in_addr); int in_localaddr(struct in_addr); int in_localip(struct in_addr); int inet_aton(const char *, struct in_addr *); /* in libkern */ char *inet_ntoa(struct in_addr); /* in libkern */ char *inet_ntoa_r(struct in_addr ina, char *buf); /* in libkern */ void in_ifdetach(struct ifnet *); #define in_hosteq(s, t) ((s).s_addr == (t).s_addr) #define in_nullhost(x) ((x).s_addr == INADDR_ANY) #define in_allhosts(x) ((x).s_addr == htonl(INADDR_ALLHOSTS_GROUP)) #define satosin(sa) ((struct sockaddr_in *)(sa)) #define sintosa(sin) ((struct sockaddr *)(sin)) #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) /* * Historically, BSD keeps ip_len and ip_off in host format * when doing layer 3 processing, and this often requires * to translate the format back and forth. * To make the process explicit, we define a couple of macros * that also take into account the fact that at some point * we may want to keep those fields always in net format. */ #if (BYTE_ORDER == BIG_ENDIAN) || defined(HAVE_NET_IPLEN) #define SET_NET_IPLEN(p) do {} while (0) #define SET_HOST_IPLEN(p) do {} while (0) #else #define SET_NET_IPLEN(p) do { \ struct ip *h_ip = (p); \ h_ip->ip_len = htons(h_ip->ip_len); \ h_ip->ip_off = htons(h_ip->ip_off); \ } while (0) #define SET_HOST_IPLEN(p) do { \ struct ip *h_ip = (p); \ h_ip->ip_len = ntohs(h_ip->ip_len); \ h_ip->ip_off = ntohs(h_ip->ip_off); \ } while (0) #endif /* !HAVE_NET_IPLEN */ #endif /* _KERNEL */ /* INET6 stuff */ #if __POSIX_VISIBLE >= 200112 #define __KAME_NETINET_IN_H_INCLUDED_ #include #undef __KAME_NETINET_IN_H_INCLUDED_ #endif #endif /* !_NETINET_IN_H_*/ pimd-2.3.2/include/freebsd/netinet/ip_mroute.h000066400000000000000000000332441267035112600213370ustar00rootroot00000000000000/*- * Copyright (c) 1989 Stephen Deering. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Stephen Deering of Stanford University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_mroute.h 8.1 (Berkeley) 6/10/93 * $FreeBSD: src/sys/netinet/ip_mroute.h,v 1.37 2010/06/02 15:44:43 zec Exp $ */ #ifndef _NETINET_IP_MROUTE_H_ #define _NETINET_IP_MROUTE_H_ /* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, SGI, June 1996. * Modified by Pavlin Radoslavov, ICSI, October 2002. * * MROUTING Revision: 3.3.1.3 * and PIM-SMv2 and PIM-DM support, advanced API support, * bandwidth metering and signaling. */ /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert processing */ #define MRT_PIM MRT_ASSERT /* enable PIM processing */ #define MRT_API_SUPPORT 109 /* supported MRT API */ #define MRT_API_CONFIG 110 /* config MRT API */ #define MRT_ADD_BW_UPCALL 111 /* create bandwidth monitor */ #define MRT_DEL_BW_UPCALL 112 /* delete bandwidth monitor */ /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_long vifbitmap_t; typedef u_short vifi_t; /* type of a vif index */ #define ALL_VIFS (vifi_t)-1 #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) struct mfc; /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_char vifc_flags; /* VIFF_ flags defined below */ u_char vifc_threshold; /* min ttl required to forward on vif */ u_int vifc_rate_limit; /* max rate */ struct in_addr vifc_lcl_addr; /* local interface address */ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */ }; #define VIFF_TUNNEL 0x1 /* no-op; retained for old source */ #define VIFF_SRCRT 0x2 /* no-op; retained for old source */ #define VIFF_REGISTER 0x4 /* used for PIM Register encap/decap */ /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC * XXX if you change this, make sure to change struct mfcctl2 as well. */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ }; /* * The new argument structure for MRT_ADD_MFC and MRT_DEL_MFC overlays * and extends the old struct mfcctl. */ struct mfcctl2 { /* the mfcctl fields */ struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ /* extension fields */ uint8_t mfcc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfcc_rp; /* the RP address */ }; /* * The advanced-API flags. * * The MRT_MFC_FLAGS_XXX API flags are also used as flags * for the mfcc_flags field. */ #define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* disable WRONGVIF signals */ #define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* border vif */ #define MRT_MFC_RP (1 << 8) /* enable RP address */ #define MRT_MFC_BW_UPCALL (1 << 9) /* enable bw upcalls */ #define MRT_MFC_FLAGS_ALL (MRT_MFC_FLAGS_DISABLE_WRONGVIF | \ MRT_MFC_FLAGS_BORDER_VIF) #define MRT_API_FLAGS_ALL (MRT_MFC_FLAGS_ALL | \ MRT_MFC_RP | \ MRT_MFC_BW_UPCALL) /* * Structure for installing or delivering an upcall if the * measured bandwidth is above or below a threshold. * * User programs (e.g. daemons) may have a need to know when the * bandwidth used by some data flow is above or below some threshold. * This interface allows the userland to specify the threshold (in * bytes and/or packets) and the measurement interval. Flows are * all packet with the same source and destination IP address. * At the moment the code is only used for multicast destinations * but there is nothing that prevents its use for unicast. * * The measurement interval cannot be shorter than some Tmin (currently, 3s). * The threshold is set in packets and/or bytes per_interval. * * Measurement works as follows: * * For >= measurements: * The first packet marks the start of a measurement interval. * During an interval we count packets and bytes, and when we * pass the threshold we deliver an upcall and we are done. * The first packet after the end of the interval resets the * count and restarts the measurement. * * For <= measurement: * We start a timer to fire at the end of the interval, and * then for each incoming packet we count packets and bytes. * When the timer fires, we compare the value with the threshold, * schedule an upcall if we are below, and restart the measurement * (reschedule timer and zero counters). */ struct bw_data { struct timeval b_time; uint64_t b_packets; uint64_t b_bytes; }; struct bw_upcall { struct in_addr bu_src; /* source address */ struct in_addr bu_dst; /* destination address */ uint32_t bu_flags; /* misc flags (see below) */ #define BW_UPCALL_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_UPCALL_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_UPCALL_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_UPCALL_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_UPCALL_DELETE_ALL (1 << 4) /* delete all upcalls for s,d*/ struct bw_data bu_threshold; /* the bw threshold */ struct bw_data bu_measured; /* the measured bw */ }; /* max. number of upcalls to deliver together */ #define BW_UPCALLS_MAX 128 /* min. threshold time interval for bandwidth measurement */ #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC 3 #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC 0 /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to multicast routing daemon */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; #ifdef _KERNEL #define MRTSTAT_ADD(name, val) V_mrtstat.name += (val) #define MRTSTAT_INC(name) MRTSTAT_ADD(name, 1) #endif /* * Argument structure used by mrouted to get src-grp pkt counts */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* Input packet count on vif */ u_long ocount; /* Output packet count on vif */ u_long ibytes; /* Input byte count on vif */ u_long obytes; /* Output byte count on vif */ }; /* * The kernel's virtual-interface structure. */ struct vif { u_char v_flags; /* VIFF_ flags defined above */ u_char v_threshold; /* min ttl required to forward on vif*/ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* cached route */ }; #ifdef _KERNEL /* * The kernel's multicast forwarding cache entry structure */ struct mfc { LIST_ENTRY(mfc) mfc_hash; struct in_addr mfc_origin; /* IP origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated*/ vifi_t mfc_parent; /* incoming vif */ u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert*/ uint8_t mfc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfc_rp; /* the RP address */ struct bw_meter *mfc_bw_meter; /* list of bandwidth meters */ u_long mfc_nstall; /* # of packets awaiting mfc */ TAILQ_HEAD(, rtdetq) mfc_stall; /* q of packets awaiting mfc */ }; #endif /* _KERNEL */ /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IP packet */ struct igmpmsg { uint32_t unused1; uint32_t unused2; u_char im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */ #define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */ #define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */ #define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */ u_char im_mbz; /* must be zero */ u_char im_vif; /* vif rec'd on */ u_char unused3; struct in_addr im_src, im_dst; }; #ifdef _KERNEL /* * Argument structure used for pkt info. while upcall is made */ struct rtdetq { TAILQ_ENTRY(rtdetq) rte_link; struct mbuf *m; /* A copy of the packet */ struct ifnet *ifp; /* Interface pkt came in on */ vifi_t xmt_vif; /* Saved copy of imo_multicast_vif */ }; #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ #endif /* _KERNEL */ /* * Structure for measuring the bandwidth and sending an upcall if the * measured bandwidth is above or below a threshold. */ struct bw_meter { struct bw_meter *bm_mfc_next; /* next bw meter (same mfc) */ struct bw_meter *bm_time_next; /* next bw meter (same time) */ uint32_t bm_time_hash; /* the time hash value */ struct mfc *bm_mfc; /* the corresponding mfc */ uint32_t bm_flags; /* misc flags (see below) */ #define BW_METER_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_METER_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_METER_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_METER_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_METER_USER_FLAGS (BW_METER_UNIT_PACKETS | \ BW_METER_UNIT_BYTES | \ BW_METER_GEQ | \ BW_METER_LEQ) #define BW_METER_UPCALL_DELIVERED (1 << 24) /* upcall was delivered */ struct bw_data bm_threshold; /* the upcall threshold */ struct bw_data bm_measured; /* the measured bw */ struct timeval bm_start_time; /* abs. time */ }; #ifdef _KERNEL struct sockopt; extern int (*ip_mrouter_set)(struct socket *, struct sockopt *); extern int (*ip_mrouter_get)(struct socket *, struct sockopt *); extern int (*ip_mrouter_done)(void); extern int (*mrt_ioctl)(u_long, caddr_t, int); #endif /* _KERNEL */ #endif /* _NETINET_IP_MROUTE_H_ */ pimd-2.3.2/include/freebsd2/000077500000000000000000000000001267035112600155715ustar00rootroot00000000000000pimd-2.3.2/include/freebsd2/netinet/000077500000000000000000000000001267035112600172375ustar00rootroot00000000000000pimd-2.3.2/include/freebsd2/netinet/in.h000066400000000000000000000327431267035112600200270ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 * $Id: in.h,v 1.22.2.1 1996/11/11 23:40:37 phk Exp $ */ #ifndef _NETINET_IN_H_ #define _NETINET_IN_H_ /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981, and numerous additions. */ /* * Protocols */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_IGMP 2 /* group mgmt protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPIP 4 /* IP encapsulation in IP */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ #define IPPROTO_RSVP 46 /* resource reservation */ #define IPPROTO_EON 80 /* ISO cnlp */ #define IPPROTO_ENCAP 98 /* encapsulation header */ #define IPPROTO_PIM 103 /* Protocol Independent Mcast */ #define IPPROTO_DIVERT 254 /* divert pseudo-protocol */ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* * Local port number conventions: * * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * The default range is IPPORT_RESERVED through * IPPORT_USERRESERVED, although that is settable by sysctl. * * A user may set the IPPROTO_IP option IP_PORTRANGE to change this * default assignment range. * * The value IP_PORTRANGE_DEFAULT causes the default behavior. * * The value IP_PORTRANGE_HIGH changes the range of candidate port numbers * into the "high" range. These are reserved for client outbound connections * which do not want to be filtered by any firewalls. * * The value IP_PORTRANGE_LOW changes the range to the "low" are * that is (by convention) restricted to privileged processes. This * convention is based on "vouchsafe" principles only. It is only secure * if you trust the remote host to restrict these ports. * * The default range of ports and the high range can be changed by * sysctl(3). (net.inet.ip.port{hi,low}{first,last}_auto) * * Changing those values has bad security implications if you are * using a a stateless firewall that is allowing packets outside of that * range in order to allow transparent outgoing connections. * * Such a firewall configuration will generally depend on the use of these * default values. If you change them, you may find your Security * Administrator looking for you with a heavy object. */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). (IP_PORTRANGE_LOW) * Ports > IPPORT_USERRESERVED are reserved * for servers, not necessarily privileged. (IP_PORTRANGE_DEFAULT) */ #define IPPORT_RESERVED 1024 #define IPPORT_USERRESERVED 5000 /* * Default local port range to use by setting IP_PORTRANGE_HIGH */ #define IPPORT_HIFIRSTAUTO 40000 #define IPPORT_HILASTAUTO 44999 /* * Scanning for a free reserved port return a value below IPPORT_RESERVED, * but higher than IPPORT_RESERVEDSTART. Traditionally the start value was * 512, but that conflicts with some well-known-services that firewalls may * have a fit if we use. */ #define IPPORT_RESERVEDSTART 600 /* * Internet address (a structure for historical reasons) */ struct in_addr { u_long s_addr; }; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) #define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ #define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ #define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((long)(i) & 0xf0000000) == 0xf0000000) #define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000) #define INADDR_ANY (u_long)0x00000000 #define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */ #ifndef KERNEL #define INADDR_NONE 0xffffffff /* -1 return */ #endif #define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP (u_long)0xe0000002 /* 224.0.0.2 */ #define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Socket address, internet style. */ struct sockaddr_in { u_char sin_len; u_char sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; /* * Structure used to describe IP options. * Used to store options internally, to pass them to a process, * or to restore options retrieved earlier. * The ip_dst is used for the first-hop gateway when using a source route * (this gets put into the header proper). */ struct ip_opts { struct in_addr ip_dst; /* first hop, 0 w/o src rt */ char ip_opts[40]; /* actually variable in size */ }; /* * Options for use with [gs]etsockopt at the IP level. * First word of comment is data type; bool is stored in int. */ #define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */ #define IP_HDRINCL 2 /* int; header is included with data */ #define IP_TOS 3 /* int; IP type of service and preced. */ #define IP_TTL 4 /* int; IP time to live */ #define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */ #define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */ #define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */ #define IP_RETOPTS 8 /* ip_opts; set/get IP options */ #define IP_MULTICAST_IF 9 /* u_char; set/get IP multicast i/f */ #define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */ #define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */ #define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */ #define IP_MULTICAST_VIF 14 /* set/get IP mcast virt. iface */ #define IP_RSVP_ON 15 /* enable RSVP in kernel */ #define IP_RSVP_OFF 16 /* disable RSVP in kernel */ #define IP_RSVP_VIF_ON 17 /* set RSVP per-vif socket */ #define IP_RSVP_VIF_OFF 18 /* unset RSVP per-vif socket */ #define IP_PORTRANGE 19 /* int; range to choose for unspec port */ #define IP_RECVIF 20 /* bool; receive reception if w/dgram */ #define IP_FW_ADD 50 /* add a firewall rule to chain */ #define IP_FW_DEL 51 /* delete a firewall rule from chain */ #define IP_FW_FLUSH 52 /* flush firewall rule chain */ #define IP_FW_ZERO 53 /* clear single/all firewall counter(s) */ #define IP_FW_GET 54 /* get entire firewall rule chain */ #define IP_NAT 55 /* set/get NAT opts */ /* * Defaults and limits for options */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Argument for IP_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IP_PORTRANGE_DEFAULT 0 /* default range */ #define IP_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IP_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ /* * Definitions for inet sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ #define IPPROTO_MAXID (IPPROTO_IDP + 1) /* don't list to IPPROTO_MAX */ #define CTL_IPPROTO_NAMES { \ { "ip", CTLTYPE_NODE }, \ { "icmp", CTLTYPE_NODE }, \ { "igmp", CTLTYPE_NODE }, \ { "ggp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { "tcp", CTLTYPE_NODE }, \ { 0, 0 }, \ { "egp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pup", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "udp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "idp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pim", CTLTYPE_NODE }, \ } /* * Names for IP sysctl objects */ #define IPCTL_FORWARDING 1 /* act as router */ #define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */ #define IPCTL_DEFTTL 3 /* default TTL */ #ifdef notyet #define IPCTL_DEFMTU 4 /* default MTU */ #endif #define IPCTL_RTEXPIRE 5 /* cloned route expiration time */ #define IPCTL_RTMINEXPIRE 6 /* min value for expiration time */ #define IPCTL_RTMAXCACHE 7 /* trigger level for dynamic expire */ #define IPCTL_SOURCEROUTE 8 /* may perform source routes */ #define IPCTL_DIRECTEDBROADCAST 9 /* may re-broadcast received packets */ #define IPCTL_INTRQMAXLEN 10 /* max length of netisr queue */ #define IPCTL_INTRQDROPS 11 /* number of netisr q drops */ #define IPCTL_MAXID 12 #define IPCTL_NAMES { \ { 0, 0 }, \ { "forwarding", CTLTYPE_INT }, \ { "redirect", CTLTYPE_INT }, \ { "ttl", CTLTYPE_INT }, \ { "mtu", CTLTYPE_INT }, \ { "rtexpire", CTLTYPE_INT }, \ { "rtminexpire", CTLTYPE_INT }, \ { "rtmaxcache", CTLTYPE_INT }, \ { "sourceroute", CTLTYPE_INT }, \ { "directed-broadcast", CTLTYPE_INT }, \ { "intr-queue-maxlen", CTLTYPE_INT }, \ { "intr-queue-drops", CTLTYPE_INT }, \ } #ifdef KERNEL struct ifnet; struct mbuf; /* forward declarations for Standard C */ int in_broadcast __P((struct in_addr, struct ifnet *)); int in_canforward __P((struct in_addr)); int in_cksum __P((struct mbuf *, int)); int in_localaddr __P((struct in_addr)); char *inet_ntoa __P((struct in_addr)); /* in libkern */ /* Firewall hooks */ struct ip; typedef int ip_fw_chk_t __P((struct ip**, int, struct ifnet*, int, struct mbuf**)); typedef int ip_fw_ctl_t __P((int, struct mbuf**)); extern ip_fw_chk_t *ip_fw_chk_ptr; extern ip_fw_ctl_t *ip_fw_ctl_ptr; /* IP NAT hooks */ typedef int ip_nat_t __P((struct ip**, struct mbuf**, struct ifnet*, int)); typedef int ip_nat_ctl_t __P((int, struct mbuf**)); extern ip_nat_t *ip_nat_ptr; extern ip_nat_ctl_t *ip_nat_ctl_ptr; #define IP_NAT_IN 0x00000001 #define IP_NAT_OUT 0x00000002 #endif /* KERNEL */ #endif pimd-2.3.2/include/freebsd2/netinet/ip_mroute.h000066400000000000000000000236361267035112600214250ustar00rootroot00000000000000/* * Copyright (c) 1989 Stephen Deering. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Stephen Deering of Stanford University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_mroute.h 8.1 (Berkeley) 6/10/93 * $Id: ip_mroute.h,v 1.10.4.1 1997/02/22 19:47:28 joerg Exp $ */ #ifndef _NETINET_IP_MROUTE_H_ #define _NETINET_IP_MROUTE_H_ /* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * * MROUTING Revision: 3.3.1.3 */ /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert (wrong iif) processing */ #define GET_TIME(t) microtime(&t) /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_long vifbitmap_t; typedef u_short vifi_t; /* type of a vif index */ #define ALL_VIFS (vifi_t)-1 #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_char vifc_flags; /* VIFF_ flags defined below */ u_char vifc_threshold; /* min ttl required to forward on vif */ u_int vifc_rate_limit; /* max rate */ struct in_addr vifc_lcl_addr; /* local interface address */ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */ }; #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP source routing */ #define VIFF_REGISTER 0x4 /* vif used for register en/decap */ #ifdef PIM_REG_KERNEL_ENCAP #define VIFF_REGISTER_KERNEL_ENCAP 0x8 /* vif register with kernel encap */ #endif /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC * (mfcc_tos to be added at a future point) */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfcc_rp_addr; /* The RP address for encap. */ #endif }; /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to mrouted */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; /* * Argument structure used by mrouted to get src-grp pkt counts */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* Input packet count on vif */ u_long ocount; /* Output packet count on vif */ u_long ibytes; /* Input byte count on vif */ u_long obytes; /* Output byte count on vif */ }; /* * The kernel's virtual-interface structure. */ struct vif { u_char v_flags; /* VIFF_ flags defined above */ u_char v_threshold; /* min ttl required to forward on vif*/ u_int v_rate_limit; /* max rate */ struct tbf *v_tbf; /* token bucket structure at intf. */ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* cached route if this is a tunnel */ u_int v_rsvp_on; /* RSVP listening on this vif */ struct socket *v_rsvpd; /* RSVP daemon socket */ }; /* * The kernel's multicast forwarding cache entry structure * (A field for the type of service (mfc_tos) is to be added * at a future point) */ struct mfc { struct in_addr mfc_origin; /* IP origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated*/ vifi_t mfc_parent; /* incoming vif */ u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert*/ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfc_rp_addr; /* The RP address for encap. */ #endif }; /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IP packet */ struct igmpmsg { u_long unused1; u_long unused2; u_char im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 #define IGMPMSG_WRONGVIF 2 #define IGMPMSG_WHOLEPKT 3 /* send the whole packet */ u_char im_mbz; /* must be zero */ u_char im_vif; /* vif rec'd on */ u_char unused3; struct in_addr im_src, im_dst; }; /* * Argument structure used for pkt info. while upcall is made */ struct rtdetq { struct mbuf *m; /* A copy of the packet */ struct ifnet *ifp; /* Interface pkt came in on */ vifi_t xmt_vif; /* Saved copy of imo_multicast_vif */ #ifdef UPCALL_TIMING struct timeval t; /* Timestamp */ #endif /* UPCALL_TIMING */ }; #define MFCTBLSIZ 256 #if (MFCTBLSIZ & (MFCTBLSIZ - 1)) == 0 /* from sys:route.h */ #define MFCHASHMOD(h) ((h) & (MFCTBLSIZ - 1)) #else #define MFCHASHMOD(h) ((h) % MFCTBLSIZ) #endif #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ /* * Token Bucket filter code */ #define MAX_BKT_SIZE 10000 /* 10K bytes size */ #define MAXQSIZE 10 /* max # of pkts in queue */ /* * the token bucket filter at each vif */ struct tbf { struct timeval tbf_last_pkt_t; /* arr. time of last pkt */ u_long tbf_n_tok; /* no of tokens in bucket */ u_long tbf_q_len; /* length of queue at this vif */ u_long tbf_max_q_len; /* max. queue length */ struct mbuf *tbf_q; /* Packet queue */ struct mbuf *tbf_t; /* tail-insertion pointer */ }; #ifdef KERNEL extern int (*ip_mrouter_set) __P((int, struct socket *, struct mbuf *)); extern int (*ip_mrouter_get) __P((int, struct socket *, struct mbuf **)); extern int (*ip_mrouter_done) __P((void)); #ifdef MROUTING extern int (*mrt_ioctl) __P((int, caddr_t)); #else extern int (*mrt_ioctl) __P((int, caddr_t, struct proc *)); #endif #endif /* KERNEL */ #endif /* _NETINET_IP_MROUTE_H_ */ pimd-2.3.2/include/linux/000077500000000000000000000000001267035112600152345ustar00rootroot00000000000000pimd-2.3.2/include/linux/netinet/000077500000000000000000000000001267035112600167025ustar00rootroot00000000000000pimd-2.3.2/include/linux/netinet/in-glibc-2.0.h000066400000000000000000000215361267035112600210430ustar00rootroot00000000000000/* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _NETINET_IN_H #define _NETINET_IN_H 1 #include #include #include __BEGIN_DECLS /* Standard well-defined IP protocols. */ enum { IPPROTO_IP = 0, /* Dummy protocol for TCP. */ IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */ IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */ IPPROTO_TCP = 6, /* Transmission Control Protocol. */ IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */ IPPROTO_PUP = 12, /* PUP protocol. */ IPPROTO_UDP = 17, /* User Datagram Protocol. */ IPPROTO_IDP = 22, /* XNS IDP protocol. */ IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling. */ IPPROTO_RSVP = 46, /* RSVP Protocol. */ IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702). */ IPPROTO_ICMPV6 = 58, /* ICMPv6. */ IPPROTO_PIM = 103, /* Protocol Independent Multicast. */ IPPROTO_RAW = 255, /* Raw IP packets. */ IPPROTO_MAX }; /* Standard well-known ports. */ enum { IPPORT_ECHO = 7, /* Echo service. */ IPPORT_DISCARD = 9, /* Discard transmissions service. */ IPPORT_SYSTAT = 11, /* System status service. */ IPPORT_DAYTIME = 13, /* Time of day service. */ IPPORT_NETSTAT = 15, /* Network status service. */ IPPORT_FTP = 21, /* File Transfer Protocol. */ IPPORT_TELNET = 23, /* Telnet protocol. */ IPPORT_SMTP = 25, /* Simple Mail Transfer Protocol. */ IPPORT_TIMESERVER = 37, /* Timeserver service. */ IPPORT_NAMESERVER = 42, /* Domain Name Service. */ IPPORT_WHOIS = 43, /* Internet Whois service. */ IPPORT_MTP = 57, IPPORT_TFTP = 69, /* Trivial File Transfer Protocol. */ IPPORT_RJE = 77, IPPORT_FINGER = 79, /* Finger service. */ IPPORT_TTYLINK = 87, IPPORT_SUPDUP = 95, /* SUPDUP protocol. */ IPPORT_EXECSERVER = 512, /* execd service. */ IPPORT_LOGINSERVER = 513, /* rlogind service. */ IPPORT_CMDSERVER = 514, IPPORT_EFSSERVER = 520, /* UDP ports. */ IPPORT_BIFFUDP = 512, IPPORT_WHOSERVER = 513, IPPORT_ROUTESERVER = 520, /* Ports less than this value are reserved for privileged processes. */ IPPORT_RESERVED = 1024, /* Ports greater this value are reserved for (non-privileged) servers. */ IPPORT_USERRESERVED = 5000 }; /* Internet address. */ struct in_addr { unsigned int s_addr; }; /* Definitions of the bits in an Internet address integer. On subnets, host and network parts are found according to the subnet mask, not these masks. */ #define IN_CLASSA(a) ((((unsigned) (a)) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) #define IN_CLASSA_MAX 128 #define IN_CLASSB(a) ((((unsigned) (a)) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) #define IN_CLASSB_MAX 65536 #define IN_CLASSC(a) ((((unsigned) (a)) & 0xc0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) #define IN_CLASSD(a) ((((unsigned) (a)) & 0xf0000000) == 0xe0000000) #define IN_MULTICAST(a) IN_CLASSD(a) #define IN_EXPERIMENTAL(a) ((((unsigned) (a)) & 0xe0000000) == 0xe0000000) #define IN_BADCLASS(a) ((((unsigned) (a)) & 0xf0000000) == 0xf0000000) /* Address to accept any incoming messages. */ #define INADDR_ANY ((unsigned) 0x00000000) /* Address to send to all hosts. */ #define INADDR_BROADCAST ((unsigned) 0xffffffff) /* Address indicating an error return. */ #define INADDR_NONE 0xffffffff /* Network number for local host loopback. */ #define IN_LOOPBACKNET 127 /* Address to loopback in software to local host. */ #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ #endif /* Defines for Multicast INADDR. */ #define INADDR_UNSPEC_GROUP ((u_int32_t) 0xe0000000U) /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP ((u_int32_t) 0xe0000001U) /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP ((u_int32_t) 0xe0000002U) /* 224.0.0.2 */ #define INADDR_MAX_LOCAL_GROUP ((u_int32_t) 0xe00000ffU) /* 224.0.0.255 */ /* Get the definition of the macro to define the common sockaddr members. */ #include /* Structure describing an Internet socket address. */ struct sockaddr_in { __SOCKADDR_COMMON (sin_); unsigned short int sin_port; /* Port number. */ struct in_addr sin_addr; /* Internet address. */ /* Pad to size of `struct sockaddr'. */ unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; /* Options for use with `getsockopt' and `setsockopt' at the IP level. The first word in the comment at the right is the data type used; "bool" means a boolean value stored in an `int'. */ #define IP_TOS 1 /* int; IP type of service and precedence. */ #define IP_TTL 2 /* int; IP time to live. */ #define IP_HDRINCL 3 /* int; Header is included with data. */ #define IP_OPTIONS 4 /* ip_opts; IP per-packet options. */ #define IP_ROUTER_ALERT 5 /* not sure. defined in linux kernel. */ #define IP_RECVOPTS 6 /* not sure. defined in linux kernel. */ #define IP_RETOPTS 7 /* not sure. defined in linux kernel. */ #define IP_PKTINFO 8 /* not sure. defined in linux kernel. */ #define IP_PKTOPTIONS 9 /* not sure. defined in linux kernel. */ #define IP_MTU_DISCOVER 10 /* not sure. defined in linux kernel. */ #define IP_RECVERR 11 /* not sure. defined in linux kernel. */ #define IP_RECVTTL 12 /* not sure. defined in linux kernel. */ #define IP_RECVTOS 13 /* not sure. defined in linux kernel. */ #define IP_MTU 14 /* not sure. defined in linux kernel. */ #define IP_MULTICAST_IF 32 /* in_addr; set/get IP multicast i/f */ #define IP_MULTICAST_TTL 33 /* u_char; set/get IP multicast ttl */ #define IP_MULTICAST_LOOP 34 /* i_char; set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 35 /* ip_mreq; add an IP group membership */ #define IP_DROP_MEMBERSHIP 36 /* ip_mreq; drop an IP group membership */ /* BSD compatibility */ #define IP_RECVRETOPTS IP_RETOPTS /* IP_MTU_DISCOVER values */ #define IP_PMTUDISC_DONT 0 /* Never send DF frames */ #define IP_PMTUDISC_WANT 1 /* Use per route hints */ #define IP_PMTUDISC_DO 2 /* Always DF */ /* To select the IP level. */ #define SOL_IP 0 /* Structure used to describe IP options for IP_OPTIONS. The `ip_dst' field is used for the first-hop gateway when using a source route (this gets put into the header proper). */ struct ip_opts { struct in_addr ip_dst; /* First hop; zero without source route. */ char ip_opts[40]; /* Actually variable in size. */ }; /* Structure used for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* Functions to convert between host and network byte order. Please note that these functions normally take `unsigned long int' or `unsigned short int' values as arguments and also return them. But this was a short-sighted decision since on different systems the types may have different representations but the values are always the same. */ extern u_int32_t ntohl __P ((u_int32_t __netlong)); extern u_int16_t ntohs __P ((u_int16_t __netshort)); extern u_int32_t htonl __P ((u_int32_t __hostlong)); extern u_int16_t htons __P ((u_int16_t __hostshort)); #include #if __BYTE_ORDER == __BIG_ENDIAN /* The host byte order is the same as network byte order, so these functions are all just identity. */ #define ntohl(x) (x) #define ntohs(x) (x) #define htonl(x) (x) #define htons(x) (x) #endif /* Bind socket to a priviledged IP port. */ extern int bindresvport __P ((int __sockfd, struct sockaddr_in *__sock_in)); __END_DECLS #endif /* netinet/in.h */ pimd-2.3.2/include/linux/netinet/in-glibc-2.1.h000066400000000000000000000245351267035112600210460ustar00rootroot00000000000000/* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _NETINET_IN_H #define _NETINET_IN_H 1 #include #include #include #include __BEGIN_DECLS /* Standard well-defined IP protocols. */ enum { IPPROTO_IP = 0, /* Dummy protocol for TCP. */ IPPROTO_HOPOPTS = 0, /* IPv6 Hop-by-Hop options. */ IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */ IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */ IPPROTO_TCP = 6, /* Transmission Control Protocol. */ IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */ IPPROTO_PUP = 12, /* PUP protocol. */ IPPROTO_UDP = 17, /* User Datagram Protocol. */ IPPROTO_IDP = 22, /* XNS IDP protocol. */ IPPROTO_IPV6 = 41, /* IPv6 header. */ IPPROTO_ROUTING = 43, /* IPv6 routing header. */ IPPROTO_FRAGMENT = 44, /* IPv6 fragmentation header. */ IPPROTO_RSVP = 46, /* RSVP Protocol. */ IPPROTO_GRE = 47, /* Cisco tunneling protocol. */ IPPROTO_ESP = 50, /* encapsulating security payload. */ IPPROTO_AH = 51, /* authentication header. */ IPPROTO_ICMPV6 = 58, /* ICMPv6. */ IPPROTO_NONE = 59, /* IPv6 no next header. */ IPPROTO_DSTOPTS = 60, /* IPv6 destination options. */ IPPROTO_PIM = 103, /* Protocol Independent Multicast. */ IPPROTO_RAW = 255, /* Raw IP packets. */ IPPROTO_MAX }; /* Standard well-known ports. */ enum { IPPORT_ECHO = 7, /* Echo service. */ IPPORT_DISCARD = 9, /* Discard transmissions service. */ IPPORT_SYSTAT = 11, /* System status service. */ IPPORT_DAYTIME = 13, /* Time of day service. */ IPPORT_NETSTAT = 15, /* Network status service. */ IPPORT_FTP = 21, /* File Transfer Protocol. */ IPPORT_TELNET = 23, /* Telnet protocol. */ IPPORT_SMTP = 25, /* Simple Mail Transfer Protocol. */ IPPORT_TIMESERVER = 37, /* Timeserver service. */ IPPORT_NAMESERVER = 42, /* Domain Name Service. */ IPPORT_WHOIS = 43, /* Internet Whois service. */ IPPORT_MTP = 57, IPPORT_TFTP = 69, /* Trivial File Transfer Protocol. */ IPPORT_RJE = 77, IPPORT_FINGER = 79, /* Finger service. */ IPPORT_TTYLINK = 87, IPPORT_SUPDUP = 95, /* SUPDUP protocol. */ IPPORT_EXECSERVER = 512, /* execd service. */ IPPORT_LOGINSERVER = 513, /* rlogind service. */ IPPORT_CMDSERVER = 514, IPPORT_EFSSERVER = 520, /* UDP ports. */ IPPORT_BIFFUDP = 512, IPPORT_WHOSERVER = 513, IPPORT_ROUTESERVER = 520, /* Ports less than this value are reserved for privileged processes. */ IPPORT_RESERVED = 1024, /* Ports greater this value are reserved for (non-privileged) servers. */ IPPORT_USERRESERVED = 5000 }; /* Internet address. */ struct in_addr { uint32_t s_addr; }; /* Definitions of the bits in an Internet address integer. On subnets, host and network parts are found according to the subnet mask, not these masks. */ #define IN_CLASSA(a) ((((uint32_t) (a)) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) #define IN_CLASSA_MAX 128 #define IN_CLASSB(a) ((((uint32_t) (a)) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) #define IN_CLASSB_MAX 65536 #define IN_CLASSC(a) ((((uint32_t) (a)) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) #define IN_CLASSD(a) ((((uint32_t) (a)) & 0xf0000000) == 0xe0000000) #define IN_MULTICAST(a) IN_CLASSD(a) #define IN_EXPERIMENTAL(a) ((((uint32_t) (a)) & 0xe0000000) == 0xe0000000) #define IN_BADCLASS(a) ((((uint32_t) (a)) & 0xf0000000) == 0xf0000000) /* Address to accept any incoming messages. */ #define INADDR_ANY ((uint32_t) 0x00000000) /* Address to send to all hosts. */ #define INADDR_BROADCAST ((uint32_t) 0xffffffff) /* Address indicating an error return. */ #define INADDR_NONE ((uint32_t) 0xffffffff) /* Network number for local host loopback. */ #define IN_LOOPBACKNET 127 /* Address to loopback in software to local host. */ #ifndef INADDR_LOOPBACK # define INADDR_LOOPBACK ((uint32_t) 0x7f000001) /* Inet 127.0.0.1. */ #endif /* Defines for Multicast INADDR. */ #define INADDR_UNSPEC_GROUP ((uint32_t) 0xe0000000) /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP ((uint32_t) 0xe0000001) /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP ((uint32_t) 0xe0000002) /* 224.0.0.2 */ #define INADDR_MAX_LOCAL_GROUP ((uint32_t) 0xe00000ff) /* 224.0.0.255 */ /* IPv6 address */ struct in6_addr { union { uint8_t u6_addr8[16]; uint16_t u6_addr16[8]; uint32_t u6_addr32[4]; #if (~0UL) > 0xffffffff uint64_t u6_addr64[2]; #endif } in6_u; #define s6_addr in6_u.u6_addr8 #define s6_addr16 in6_u.u6_addr16 #define s6_addr32 in6_u.u6_addr32 #define s6_addr64 in6_u.u6_addr64 }; extern const struct in6_addr in6addr_any; /* :: */ extern const struct in6_addr in6addr_loopback; /* ::1 */ #define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } #define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } #define INET_ADDRSTRLEN 16 #define INET6_ADDRSTRLEN 46 /* Get the definition of the macro to define the common sockaddr members. */ #include /* Structure describing an Internet socket address. */ struct sockaddr_in { __SOCKADDR_COMMON (sin_); uint16_t sin_port; /* Port number. */ struct in_addr sin_addr; /* Internet address. */ /* Pad to size of `struct sockaddr'. */ unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (uint16_t) - sizeof (struct in_addr)]; }; /* Ditto, for IPv6. */ struct sockaddr_in6 { __SOCKADDR_COMMON (sin6_); uint16_t sin6_port; /* Transport layer port # */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ }; /* IPv6 multicast request. */ struct ipv6_mreq { /* IPv6 multicast address of group */ struct in6_addr ipv6mr_multiaddr; /* local interface */ unsigned int ipv6mr_ifindex; }; /* Get system-specific definitions. */ #include /* Functions to convert between host and network byte order. Please note that these functions normally take `unsigned long int' or `unsigned short int' values as arguments and also return them. But this was a short-sighted decision since on different systems the types may have different representations but the values are always the same. */ extern uint32_t ntohl __P ((uint32_t __netlong)); extern uint16_t ntohs __P ((uint16_t __netshort)); extern uint32_t htonl __P ((uint32_t __hostlong)); extern uint16_t htons __P ((uint16_t __hostshort)); #include /* Get machine dependent optimized versions of byte swapping functions. */ #include #if __BYTE_ORDER == __BIG_ENDIAN && defined __OPTIMIZE__ /* The host byte order is the same as network byte order, so these functions are all just identity. */ # define ntohl(x) (x) # define ntohs(x) (x) # define htonl(x) (x) # define htons(x) (x) #else # if __BYTE_ORDER == __LITTLE_ENDIAN && defined __OPTIMIZE__ # define ntohl(x) __bswap_32 (x) # define ntohs(x) __bswap_16 (x) # define htonl(x) __bswap_32 (x) # define htons(x) __bswap_16 (x) # endif #endif #define IN6_IS_ADDR_UNSPECIFIED(a) \ (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0) #define IN6_IS_ADDR_LOOPBACK(a) \ (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == htonl (1)) #define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff) #define IN6_IS_ADDR_LINKLOCAL(a) \ ((((uint32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000)) #define IN6_IS_ADDR_SITELOCAL(a) \ ((((uint32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfec00000)) #define IN6_IS_ADDR_V4MAPPED(a) \ ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \ (((uint32_t *) (a))[2] == htonl (0xffff))) #define IN6_IS_ADDR_V4COMPAT(a) \ ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \ (((uint32_t *) (a))[2] == 0) && (ntohl (((uint32_t *) (a))[3]) > 1)) #define IN6_ARE_ADDR_EQUAL(a,b) \ ((((uint32_t *) (a))[0] == ((uint32_t *) (b))[0]) && \ (((uint32_t *) (a))[1] == ((uint32_t *) (b))[2]) && \ (((uint32_t *) (a))[2] == ((uint32_t *) (b))[1]) && \ (((uint32_t *) (a))[3] == ((uint32_t *) (b))[3])) /* Bind socket to a privileged IP port. */ extern int bindresvport __P ((int __sockfd, struct sockaddr_in *__sock_in)); #define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((u_int8_t *) (a))[1] & 0xf) == 0x1)) #define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((u_int8_t *) (a))[1] & 0xf) == 0x2)) #define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((u_int8_t *) (a))[1] & 0xf) == 0x5)) #define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((u_int8_t *) (a))[1] & 0xf) == 0x8)) #define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((u_int8_t *) (a))[1] & 0xf) == 0xe)) /* IPv6 packet information. */ struct in6_pktinfo { struct in6_addr ipi6_addr; /* src/dst IPv6 address */ unsigned int ipi6_ifindex; /* send/recv interface index */ }; __END_DECLS #endif /* netinet/in.h */ pimd-2.3.2/include/linux/netinet/in-my.h000066400000000000000000000014611267035112600201060ustar00rootroot00000000000000/* * Dummy header file to include the appropriate in.h for Linux * The situation is pretty messy, and no guarantee it will work. * Use your skills and imagination at your own risk :) * * Thanks to Jonathan Day for the problem report and the solution * */ /* * Questions concerning this software should be directed to * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu) * * $Id: in.h,v 1.8 2000/03/08 09:12:45 pavlin Exp $ */ #include #if (defined(__GLIBC__) && (defined(__GLIBC_MINOR__))) # if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) # include "in-glibc-2.0.h" # elif (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 1) # include "in-glibc-2.1.h" # else # include # include # endif /* __GLIBC__ */ #else # include # include #endif pimd-2.3.2/include/linux/netinet/ip_mroute.h000066400000000000000000000000251267035112600210530ustar00rootroot00000000000000#include "mroute.h" pimd-2.3.2/include/linux/netinet/mroute.h000066400000000000000000000074211267035112600203720ustar00rootroot00000000000000#ifndef __LINUX_MROUTE_H #define __LINUX_MROUTE_H #include #include /* * Based on the MROUTING 3.5 defines primarily to keep * source compatibility with BSD. * * See the mrouted code for the original history. * * Protocol Independent Multicast (PIM) data structures included * Carlos Picoto (cap@di.fc.ul.pt) * */ #define MRT_BASE 200 #define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */ #define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */ #define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */ #define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */ #define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */ #define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */ #define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */ #define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ #define MRT_PIM (MRT_BASE+8) /* enable PIM code */ #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) #define SIOCGETRPF (SIOCPROTOPRIVATE+2) #ifdef CUSTOM_MAX_VIFS #define MAXVIFS CUSTOM_MAX_VIFS /* MUST match kernel MAXVIFS! */ #else #define MAXVIFS 32 #endif typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */ typedef unsigned short vifi_t; #define ALL_VIFS ((vifi_t)(-1)) /* * Same idea as select */ #define VIFM_SET(n,m) ((m)|=(1<<(n))) #define VIFM_CLR(n,m) ((m)&=~(1<<(n))) #define VIFM_ISSET(n,m) ((m)&(1<<(n))) #define VIFM_CLRALL(m) ((m)=0) #define VIFM_COPY(mfrom,mto) ((mto)=(mfrom)) #define VIFM_SAME(m1,m2) ((m1)==(m2)) /* * Passed by mrouted for an MRT_ADD_VIF - again we use the * mrouted 3.6 structures for compatibility */ struct vifctl { vifi_t vifc_vifi; /* Index of VIF */ unsigned char vifc_flags; /* VIFF_ flags */ unsigned char vifc_threshold; /* ttl limit */ unsigned int vifc_rate_limit; /* Rate limiter values (NI) */ struct in_addr vifc_lcl_addr; /* Our address */ struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */ }; #define VIFF_TUNNEL 0x1 /* IPIP tunnel */ #define VIFF_SRCRT 0x2 /* NI */ #define VIFF_REGISTER 0x4 /* register vif */ /* * Cache manipulation structures for mrouted and PIMd */ struct mfcctl { struct in_addr mfcc_origin; /* Origin of mcast */ struct in_addr mfcc_mcastgrp; /* Group in question */ vifi_t mfcc_parent; /* Where it arrived */ unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */ unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */ unsigned int mfcc_byte_cnt; unsigned int mfcc_wrong_if; int mfcc_expire; }; /* * Group count retrieval for mrouted */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; unsigned long pktcnt; unsigned long bytecnt; unsigned long wrong_if; }; /* * To get vif packet counts */ struct sioc_vif_req { vifi_t vifi; /* Which iface */ unsigned long icount; /* In packets */ unsigned long ocount; /* Out packets */ unsigned long ibytes; /* In bytes */ unsigned long obytes; /* Out bytes */ }; /* * This is the format the mroute daemon expects to see IGMP control * data. Magically happens to be like an IP packet as per the original */ struct igmpmsg { uint32_t unused1,unused2; unsigned char im_msgtype; /* What is this */ unsigned char im_mbz; /* Must be zero */ unsigned char im_vif; /* Interface (this ought to be a vifi_t!) */ unsigned char unused3; struct in_addr im_src,im_dst; }; /* * That's all usermode folks */ #define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */ /* * Pseudo messages used by mrouted */ #define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */ #define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */ #define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */ #endif pimd-2.3.2/include/netbsd/000077500000000000000000000000001267035112600153545ustar00rootroot00000000000000pimd-2.3.2/include/netbsd/netinet/000077500000000000000000000000001267035112600170225ustar00rootroot00000000000000pimd-2.3.2/include/netbsd/netinet/in.h000066400000000000000000000441021267035112600176020ustar00rootroot00000000000000/* $NetBSD: in.h,v 1.86 2009/09/14 10:36:50 degroote Exp $ */ /* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 */ /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981, and numerous additions. */ #ifndef _NETINET_IN_H_ #define _NETINET_IN_H_ #include #ifndef uint8_t typedef __uint8_t uint8_t; #define uint8_t __uint8_t #endif #ifndef uint32_t typedef __uint32_t uint32_t; #define uint32_t __uint32_t #endif #include #ifndef in_addr_t typedef __in_addr_t in_addr_t; #define in_addr_t __in_addr_t #endif #ifndef in_port_t typedef __in_port_t in_port_t; #define in_port_t __in_port_t #endif #ifndef sa_family_t typedef __sa_family_t sa_family_t; #define sa_family_t __sa_family_t #endif /* * Protocols */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_HOPOPTS 0 /* IP6 hop-by-hop options */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_IGMP 2 /* group mgmt protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPV4 4 /* IP header */ #define IPPROTO_IPIP 4 /* IP inside IP */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ #define IPPROTO_IPV6 41 /* IP6 header */ #define IPPROTO_ROUTING 43 /* IP6 routing header */ #define IPPROTO_FRAGMENT 44 /* IP6 fragmentation header */ #define IPPROTO_RSVP 46 /* resource reservation */ #define IPPROTO_GRE 47 /* GRE encaps RFC 1701 */ #define IPPROTO_ESP 50 /* encap. security payload */ #define IPPROTO_AH 51 /* authentication header */ #define IPPROTO_MOBILE 55 /* IP Mobility RFC 2004 */ #define IPPROTO_IPV6_ICMP 58 /* IPv6 ICMP */ #define IPPROTO_ICMPV6 58 /* ICMP6 */ #define IPPROTO_NONE 59 /* IP6 no next header */ #define IPPROTO_DSTOPTS 60 /* IP6 destination option */ #define IPPROTO_EON 80 /* ISO cnlp */ #define IPPROTO_ETHERIP 97 /* Ethernet-in-IP */ #define IPPROTO_ENCAP 98 /* encapsulation header */ #define IPPROTO_PIM 103 /* Protocol indep. multicast */ #define IPPROTO_IPCOMP 108 /* IP Payload Comp. Protocol */ #define IPPROTO_VRRP 112 /* VRRP RFC 2338 */ #define IPPROTO_CARP 112 /* Common Address Resolution Protocol */ #define IPPROTO_PFSYNC 240 /* PFSYNC */ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* last return value of *_input(), meaning "all job for this pkt is done". */ #define IPPROTO_DONE 257 /* sysctl placeholder for (FAST_)IPSEC */ #define CTL_IPPROTO_IPSEC 258 /* * Local port number conventions: * * Ports < IPPORT_RESERVED are reserved for privileged processes (e.g. root), * unless a kernel is compiled with IPNOPRIVPORTS defined. * * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * * The default range is IPPORT_ANONMIN to IPPORT_ANONMAX, although * that is settable by sysctl(3); net.inet.ip.anonportmin and * net.inet.ip.anonportmax respectively. * * A user may set the IPPROTO_IP option IP_PORTRANGE to change this * default assignment range. * * The value IP_PORTRANGE_DEFAULT causes the default behavior. * * The value IP_PORTRANGE_HIGH is the same as IP_PORTRANGE_DEFAULT, * and exists only for FreeBSD compatibility purposes. * * The value IP_PORTRANGE_LOW changes the range to the "low" are * that is (by convention) restricted to privileged processes. * This convention is based on "vouchsafe" principles only. * It is only secure if you trust the remote host to restrict these ports. * The range is IPPORT_RESERVEDMIN to IPPORT_RESERVEDMAX. */ #define IPPORT_RESERVED 1024 #define IPPORT_ANONMIN 49152 #define IPPORT_ANONMAX 65535 #define IPPORT_RESERVEDMIN 600 #define IPPORT_RESERVEDMAX (IPPORT_RESERVED-1) /* * Internet address (a structure for historical reasons) */ struct in_addr { in_addr_t s_addr; } __packed; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. * * By byte-swapping the constants, we avoid ever having to byte-swap IP * addresses inside the kernel. Unfortunately, user-level programs rely * on these macros not doing byte-swapping. */ #ifdef _KERNEL #define __IPADDR(x) ((uint32_t) htonl((uint32_t)(x))) #else #define __IPADDR(x) ((uint32_t)(x)) #endif #define IN_CLASSA(i) (((uint32_t)(i) & __IPADDR(0x80000000)) == \ __IPADDR(0x00000000)) #define IN_CLASSA_NET __IPADDR(0xff000000) #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST __IPADDR(0x00ffffff) #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((uint32_t)(i) & __IPADDR(0xc0000000)) == \ __IPADDR(0x80000000)) #define IN_CLASSB_NET __IPADDR(0xffff0000) #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST __IPADDR(0x0000ffff) #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((uint32_t)(i) & __IPADDR(0xe0000000)) == \ __IPADDR(0xc0000000)) #define IN_CLASSC_NET __IPADDR(0xffffff00) #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST __IPADDR(0x000000ff) #define IN_CLASSD(i) (((uint32_t)(i) & __IPADDR(0xf0000000)) == \ __IPADDR(0xe0000000)) /* These ones aren't really net and host fields, but routing needn't know. */ #define IN_CLASSD_NET __IPADDR(0xf0000000) #define IN_CLASSD_NSHIFT 28 #define IN_CLASSD_HOST __IPADDR(0x0fffffff) #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((uint32_t)(i) & __IPADDR(0xf0000000)) == \ __IPADDR(0xf0000000)) #define IN_BADCLASS(i) (((uint32_t)(i) & __IPADDR(0xf0000000)) == \ __IPADDR(0xf0000000)) #define IN_LINKLOCAL(i) (((uint32_t)(i) & __IPADDR(0xffff0000)) == \ __IPADDR(0xa9fe0000)) #define IN_PRIVATE(i) ((((uint32_t)(i) & __IPADDR(0xff000000)) == \ __IPADDR(0x0a000000)) || \ (((uint32_t)(i) & __IPADDR(0xfff00000)) == \ __IPADDR(0xac100000)) || \ (((uint32_t)(i) & __IPADDR(0xffff0000)) == \ __IPADDR(0xc0a80000))) #define IN_LOCAL_GROUP(i) (((uint32_t)(i) & __IPADDR(0xffffff00)) == \ __IPADDR(0xe0000000)) #define IN_ANY_LOCAL(i) (IN_LINKLOCAL(i) || IN_LOCAL_GROUP(i)) #define INADDR_ANY __IPADDR(0x00000000) #define INADDR_LOOPBACK __IPADDR(0x7f000001) #define INADDR_BROADCAST __IPADDR(0xffffffff) /* must be masked */ #define INADDR_NONE __IPADDR(0xffffffff) /* -1 return */ #define INADDR_UNSPEC_GROUP __IPADDR(0xe0000000) /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP __IPADDR(0xe0000001) /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP __IPADDR(0xe0000002) /* 224.0.0.2 */ #define INADDR_CARP_GROUP __IPADDR(0xe0000012) /* 224.0.0.18 */ #define INADDR_MAX_LOCAL_GROUP __IPADDR(0xe00000ff) /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Socket address, internet style. */ struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; __int8_t sin_zero[8]; }; #define INET_ADDRSTRLEN 16 /* * Structure used to describe IP options. * Used to store options internally, to pass them to a process, * or to restore options retrieved earlier. * The ip_dst is used for the first-hop gateway when using a source route * (this gets put into the header proper). */ struct ip_opts { struct in_addr ip_dst; /* first hop, 0 w/o src rt */ #if defined(__cplusplus) __int8_t Ip_opts[40]; /* actually variable in size */ #else __int8_t ip_opts[40]; /* actually variable in size */ #endif }; /* * Options for use with [gs]etsockopt at the IP level. * First word of comment is data type; bool is stored in int. */ #define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */ #define IP_HDRINCL 2 /* int; header is included with data */ #define IP_TOS 3 /* int; IP type of service and preced. */ #define IP_TTL 4 /* int; IP time to live */ #define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */ #define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */ #define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */ #define IP_RETOPTS 8 /* ip_opts; set/get IP options */ #define IP_MULTICAST_IF 9 /* in_addr; set/get IP multicast i/f */ #define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */ #define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */ #define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */ #define IP_PORTRANGE 19 /* int; range to use for ephemeral port */ #define IP_RECVIF 20 /* bool; receive reception if w/dgram */ #define IP_ERRORMTU 21 /* int; get MTU of last xmit = EMSGSIZE */ #if 1 /*IPSEC*/ #define IP_IPSEC_POLICY 22 /* struct; get/set security policy */ #endif #define IP_RECVTTL 23 /* bool; receive IP TTL w/dgram */ #define IP_MINTTL 24 /* minimum TTL for packet or drop */ /* * Defaults and limits for options */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Argument for IP_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IP_PORTRANGE_DEFAULT 0 /* default range */ #define IP_PORTRANGE_HIGH 1 /* same as DEFAULT (FreeBSD compat) */ #define IP_PORTRANGE_LOW 2 /* use privileged range */ #if defined(_NETBSD_SOURCE) /* * Definitions for inet sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ #define IPPROTO_MAXID (IPPROTO_AH + 1) /* don't list to IPPROTO_MAX */ #define CTL_IPPROTO_NAMES { \ { "ip", CTLTYPE_NODE }, \ { "icmp", CTLTYPE_NODE }, \ { "igmp", CTLTYPE_NODE }, \ { "ggp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { "tcp", CTLTYPE_NODE }, \ { 0, 0 }, \ { "egp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pup", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "udp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "idp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "ipsec", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pim", CTLTYPE_NODE }, \ } /* * Names for IP sysctl objects */ #define IPCTL_FORWARDING 1 /* act as router */ #define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */ #define IPCTL_DEFTTL 3 /* default TTL */ #ifdef notyet #define IPCTL_DEFMTU 4 /* default MTU */ #endif #define IPCTL_FORWSRCRT 5 /* forward source-routed packets */ #define IPCTL_DIRECTEDBCAST 6 /* default broadcast behavior */ #define IPCTL_ALLOWSRCRT 7 /* allow/drop all source-routed pkts */ #define IPCTL_SUBNETSARELOCAL 8 /* treat subnets as local addresses */ #define IPCTL_MTUDISC 9 /* allow path MTU discovery */ #define IPCTL_ANONPORTMIN 10 /* minimum ephemeral port */ #define IPCTL_ANONPORTMAX 11 /* maximum ephemeral port */ #define IPCTL_MTUDISCTIMEOUT 12 /* allow path MTU discovery */ #define IPCTL_MAXFLOWS 13 /* maximum ip flows allowed */ #define IPCTL_HOSTZEROBROADCAST 14 /* is host zero a broadcast addr? */ #define IPCTL_GIF_TTL 15 /* default TTL for gif encap packet */ #define IPCTL_LOWPORTMIN 16 /* minimum reserved port */ #define IPCTL_LOWPORTMAX 17 /* maximum reserved port */ #define IPCTL_MAXFRAGPACKETS 18 /* max packets reassembly queue */ #define IPCTL_GRE_TTL 19 /* default TTL for gre encap packet */ #define IPCTL_CHECKINTERFACE 20 /* drop pkts in from 'wrong' iface */ #define IPCTL_IFQ 21 /* ipintrq node */ #define IPCTL_RANDOMID 22 /* use random IP ids (if configured) */ #define IPCTL_LOOPBACKCKSUM 23 /* do IP checksum on loopback */ #define IPCTL_STATS 24 /* IP statistics */ #define IPCTL_MAXID 25 #define IPCTL_NAMES { \ { 0, 0 }, \ { "forwarding", CTLTYPE_INT }, \ { "redirect", CTLTYPE_INT }, \ { "ttl", CTLTYPE_INT }, \ { "mtu", CTLTYPE_INT }, \ { "forwsrcrt", CTLTYPE_INT }, \ { "directed-broadcast", CTLTYPE_INT }, \ { "allowsrcrt", CTLTYPE_INT }, \ { "subnetsarelocal", CTLTYPE_INT }, \ { "mtudisc", CTLTYPE_INT }, \ { "anonportmin", CTLTYPE_INT }, \ { "anonportmax", CTLTYPE_INT }, \ { "mtudisctimeout", CTLTYPE_INT }, \ { "maxflows", CTLTYPE_INT }, \ { "hostzerobroadcast", CTLTYPE_INT }, \ { "gifttl", CTLTYPE_INT }, \ { "lowportmin", CTLTYPE_INT }, \ { "lowportmax", CTLTYPE_INT }, \ { "maxfragpackets", CTLTYPE_INT }, \ { "grettl", CTLTYPE_INT }, \ { "checkinterface", CTLTYPE_INT }, \ { "ifq", CTLTYPE_NODE }, \ { "random_id", CTLTYPE_INT }, \ { "do_loopback_cksum", CTLTYPE_INT }, \ { "stats", CTLTYPE_STRUCT }, \ } #endif /* _NETBSD_SOURCE */ /* INET6 stuff */ #define __KAME_NETINET_IN_H_INCLUDED_ #include #undef __KAME_NETINET_IN_H_INCLUDED_ #ifdef _KERNEL /* * in_cksum_phdr: * * Compute significant parts of the IPv4 checksum pseudo-header * for use in a delayed TCP/UDP checksum calculation. * * Args: * * src Source IP address * dst Destination IP address * lenproto htons(proto-hdr-len + proto-number) */ static __inline u_int16_t __unused in_cksum_phdr(u_int32_t src, u_int32_t dst, u_int32_t lenproto) { u_int32_t sum; sum = lenproto + (u_int16_t)(src >> 16) + (u_int16_t)(src /*& 0xffff*/) + (u_int16_t)(dst >> 16) + (u_int16_t)(dst /*& 0xffff*/); sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/); if (sum > 0xffff) sum -= 0xffff; return (sum); } /* * in_cksum_addword: * * Add the two 16-bit network-order values, carry, and return. */ static __inline u_int16_t __unused in_cksum_addword(u_int16_t a, u_int16_t b) { u_int32_t sum = a + b; if (sum > 0xffff) sum -= 0xffff; return (sum); } extern struct in_addr zeroin_addr; extern u_char ip_protox[]; extern const struct sockaddr_in in_any; int in_broadcast(struct in_addr, struct ifnet *); int in_canforward(struct in_addr); int cpu_in_cksum(struct mbuf *, int, int, uint32_t); int in_cksum(struct mbuf *, int); int in4_cksum(struct mbuf *, u_int8_t, int, int); void in_delayed_cksum(struct mbuf *); int in_localaddr(struct in_addr); void in_socktrim(struct sockaddr_in *); #define in_hosteq(s,t) ((s).s_addr == (t).s_addr) #define in_nullhost(x) ((x).s_addr == INADDR_ANY) #define satosin(sa) ((struct sockaddr_in *)(sa)) #define satocsin(sa) ((const struct sockaddr_in *)(sa)) #define sintosa(sin) ((struct sockaddr *)(sin)) #define sintocsa(sin) ((const struct sockaddr *)(sin)) #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) int sockaddr_in_cmp(const struct sockaddr *, const struct sockaddr *); const void *sockaddr_in_const_addr(const struct sockaddr *, socklen_t *); void *sockaddr_in_addr(struct sockaddr *, socklen_t *); static inline void sockaddr_in_init1(struct sockaddr_in *sin, const struct in_addr *addr, in_port_t port) { sin->sin_port = port; sin->sin_addr = *addr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } static inline void sockaddr_in_init(struct sockaddr_in *sin, const struct in_addr *addr, in_port_t port) { sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sockaddr_in_init1(sin, addr, port); } static inline struct sockaddr * sockaddr_in_alloc(const struct in_addr *addr, in_port_t port, int flags) { struct sockaddr *sa; sa = sockaddr_alloc(AF_INET, sizeof(struct sockaddr_in), flags); if (sa == NULL) return NULL; sockaddr_in_init1(satosin(sa), addr, port); return sa; } #endif /* _KERNEL */ #endif /* !_NETINET_IN_H_ */ pimd-2.3.2/include/netbsd/netinet/ip_mroute.h000066400000000000000000000313471267035112600212060ustar00rootroot00000000000000/* $NetBSD: ip_mroute.h,v 1.31 2008/08/07 06:20:14 cegger Exp $ */ #ifndef _NETINET_IP_MROUTE_H_ #define _NETINET_IP_MROUTE_H_ /* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, SGI, June 1996. * Modified by Pavlin Radoslavov, ICSI, October 2002. * * MROUTING Revision: 1.2 * and PIM-SMv2 and PIM-DM support, advanced API support, * bandwidth metering and signaling. */ #include #include #ifdef _KERNEL struct sockopt; /* from */ #endif /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert processing */ #define MRT_PIM MRT_ASSERT /* enable PIM processing */ #define MRT_API_SUPPORT 109 /* supported MRT API */ #define MRT_API_CONFIG 110 /* config MRT API */ #define MRT_ADD_BW_UPCALL 111 /* create bandwidth monitor */ #define MRT_DEL_BW_UPCALL 112 /* delete bandwidth monitor */ /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_int32_t vifbitmap_t; typedef u_int16_t vifi_t; /* type of a vif index */ #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_SETALL(m) ((m) = 0xffffffff) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */ #define VIFF_REGISTER 0x4 /* used for PIM Register encap/decap */ /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_int8_t vifc_flags; /* VIFF_ flags defined below */ u_int8_t vifc_threshold; /* min ttl required to forward on vif */ u_int32_t vifc_rate_limit; /* max rate */ struct in_addr vifc_lcl_addr;/* local interface address */ struct in_addr vifc_rmt_addr;/* remote address (tunnels only) */ }; /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC. * XXX if you change this, make sure to change struct mfcctl2 as well. */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated */ vifi_t mfcc_parent; /* incoming vif */ u_int8_t mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ }; /* * The new argument structure for MRT_ADD_MFC and MRT_DEL_MFC overlays * and extends the old struct mfcctl. */ struct mfcctl2 { /* the mfcctl fields */ struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_int8_t mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ /* extension fields */ u_int8_t mfcc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfcc_rp; /* the RP address */ }; /* * The advanced-API flags. * * The MRT_MFC_FLAGS_XXX API flags are also used as flags * for the mfcc_flags field. */ #define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* disable WRONGVIF signals */ #define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* border vif */ #define MRT_MFC_RP (1 << 8) /* enable RP address */ #define MRT_MFC_BW_UPCALL (1 << 9) /* enable bw upcalls */ #define MRT_MFC_FLAGS_ALL (MRT_MFC_FLAGS_DISABLE_WRONGVIF | \ MRT_MFC_FLAGS_BORDER_VIF) #define MRT_API_FLAGS_ALL (MRT_MFC_FLAGS_ALL | \ MRT_MFC_RP | \ MRT_MFC_BW_UPCALL) /* * Structure for installing or delivering an upcall if the * measured bandwidth is above or below a threshold. * * User programs (e.g. daemons) may have a need to know when the * bandwidth used by some data flow is above or below some threshold. * This interface allows the userland to specify the threshold (in * bytes and/or packets) and the measurement interval. Flows are * all packet with the same source and destination IP address. * At the moment the code is only used for multicast destinations * but there is nothing that prevents its use for unicast. * * The measurement interval cannot be shorter than some Tmin (currently, 3s). * The threshold is set in packets and/or bytes per_interval. * * Measurement works as follows: * * For >= measurements: * The first packet marks the start of a measurement interval. * During an interval we count packets and bytes, and when we * pass the threshold we deliver an upcall and we are done. * The first packet after the end of the interval resets the * count and restarts the measurement. * * For <= measurement: * We start a timer to fire at the end of the interval, and * then for each incoming packet we count packets and bytes. * When the timer fires, we compare the value with the threshold, * schedule an upcall if we are below, and restart the measurement * (reschedule timer and zero counters). */ struct bw_data { struct timeval b_time; u_int64_t b_packets; u_int64_t b_bytes; }; struct bw_upcall { struct in_addr bu_src; /* source address */ struct in_addr bu_dst; /* destination address */ u_int32_t bu_flags; /* misc flags (see below) */ #define BW_UPCALL_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_UPCALL_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_UPCALL_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_UPCALL_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_UPCALL_DELETE_ALL (1 << 4) /* delete all upcalls for s,d*/ struct bw_data bu_threshold; /* the bw threshold */ struct bw_data bu_measured; /* the measured bw */ }; /* max. number of upcalls to deliver together */ #define BW_UPCALLS_MAX 128 /* min. threshold time interval for bandwidth measurement */ #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC 3 #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC 0 /* * Argument structure used by mrouted to get src-grp pkt counts. */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts. */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* input packet count on vif */ u_long ocount; /* output packet count on vif */ u_long ibytes; /* input byte count on vif */ u_long obytes; /* output byte count on vif */ }; /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to mrouted */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; #ifdef _KERNEL /* * The kernel's virtual-interface structure. */ struct encaptab; struct vif { struct mbuf *tbf_q, **tbf_t; /* packet queue */ struct timeval tbf_last_pkt_t; /* arr. time of last pkt */ u_int32_t tbf_n_tok; /* no of tokens in bucket */ u_int32_t tbf_q_len; /* length of queue at this vif */ u_int32_t tbf_max_q_len; /* max. queue length */ u_int8_t v_flags; /* VIFF_ flags defined above */ u_int8_t v_threshold; /* min ttl required to forward on vif */ u_int32_t v_rate_limit; /* max rate */ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* cached route if this is a tunnel */ callout_t v_repq_ch; /* for tbf_reprocess_q() */ #ifdef RSVP_ISI int v_rsvp_on; /* # RSVP listening on this vif */ struct socket *v_rsvpd; /* # RSVPD daemon */ #endif /* RSVP_ISI */ const struct encaptab *v_encap_cookie; }; /* * The kernel's multicast forwarding cache entry structure. * (A field for the type of service (mfc_tos) is to be added * at a future point.) */ struct mfc { LIST_ENTRY(mfc) mfc_hash; struct in_addr mfc_origin; /* ip origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated */ vifi_t mfc_parent; /* incoming vif */ u_int8_t mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert */ struct rtdetq *mfc_stall; /* pkts waiting for route */ u_int8_t mfc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfc_rp; /* the RP address */ struct bw_meter *mfc_bw_meter; /* list of bandwidth meters */ }; /* * Structure used to communicate from kernel to multicast router. * (Note the convenient similarity to an IP packet.) */ struct igmpmsg { u_int32_t unused1; u_int32_t unused2; u_int8_t im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */ #define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */ #define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */ #define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */ u_int8_t im_mbz; /* must be zero */ u_int8_t im_vif; /* vif rec'd on */ u_int8_t unused3; struct in_addr im_src, im_dst; } __packed; /* * Argument structure used for pkt info. while upcall is made. */ struct rtdetq { struct mbuf *m; /* a copy of the packet */ struct ifnet *ifp; /* interface pkt came in on */ #ifdef UPCALL_TIMING struct timeval t; /* timestamp */ #endif /* UPCALL_TIMING */ struct rtdetq *next; }; #define MFCTBLSIZ 256 #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ /* * Token bucket filter code */ #define MAX_BKT_SIZE 10000 /* 10K bytes size */ #define MAXQSIZE 10 /* max. no of pkts in token queue */ /* * Structure for measuring the bandwidth and sending an upcall if the * measured bandwidth is above or below a threshold. */ struct bw_meter { struct bw_meter *bm_mfc_next; /* next bw meter (same mfc) */ struct bw_meter *bm_time_next; /* next bw meter (same time) */ uint32_t bm_time_hash; /* the time hash value */ struct mfc *bm_mfc; /* the corresponding mfc */ uint32_t bm_flags; /* misc flags (see below) */ #define BW_METER_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_METER_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_METER_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_METER_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_METER_USER_FLAGS (BW_METER_UNIT_PACKETS | \ BW_METER_UNIT_BYTES | \ BW_METER_GEQ | \ BW_METER_LEQ) #define BW_METER_UPCALL_DELIVERED (1 << 24) /* upcall was delivered */ struct bw_data bm_threshold; /* the upcall threshold */ struct bw_data bm_measured; /* the measured bw */ struct timeval bm_start_time; /* abs. time */ }; int ip_mrouter_set(struct socket *, struct sockopt *); int ip_mrouter_get(struct socket *, struct sockopt *); int mrt_ioctl(struct socket *, u_long, void *); int ip_mrouter_done(void); void ip_mrouter_detach(struct ifnet *); void reset_vif(struct vif *); #ifdef RSVP_ISI int ip_mforward(struct mbuf *, struct ifnet *, struct ip_moptions *); int legal_vif_num(int); int ip_rsvp_vif_init(struct socket *, struct mbuf *); int ip_rsvp_vif_done(struct socket *, struct mbuf *); void ip_rsvp_force_done(struct socket *); void rsvp_input(struct mbuf *, int, int); #else int ip_mforward(struct mbuf *, struct ifnet *); #endif #endif /* _KERNEL */ #endif /* !_NETINET_IP_MROUTE_H_ */ pimd-2.3.2/include/netinet/000077500000000000000000000000001267035112600155435ustar00rootroot00000000000000pimd-2.3.2/include/netinet/pim.h000066400000000000000000000102611267035112600165010ustar00rootroot00000000000000/* * Copyright (c) 1996-2000 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETINET_PIM_H_ #define _NETINET_PIM_H_ /* * Protocol Independent Multicast (PIM) definitions. * RFC 2362, June 1998. * * Written by Ahmed Helmy, USC/SGI, July 1996. * Modified by George Edmond Eddy (Rusty), ISI, February 1998. * Modified by Pavlin Radoslavov, USC/ISI, May 1998, October 2000. */ #ifndef _PIM_VT #ifndef BYTE_ORDER # error BYTE_ORDER is not defined! #endif #if (BYTE_ORDER != BIG_ENDIAN) && (BYTE_ORDER != LITTLE_ENDIAN) # error BYTE_ORDER must be defined to either BIG_ENDIAN or LITTLE_ENDIAN #endif #endif /* ! _PIM_VT */ /* * PIM packet header */ struct pim { #ifdef _PIM_VT uint8_t pim_vt; /* PIM version and message type */ #else /* ! _PIM_VT */ #if BYTE_ORDER == BIG_ENDIAN u_int pim_vers:4, /* PIM protocol version */ pim_type:4; /* PIM message type */ #endif #if BYTE_ORDER == LITTLE_ENDIAN u_int pim_type:4, /* PIM message type */ pim_vers:4; /* PIM protocol version */ #endif #endif /* ! _PIM_VT */ uint8_t pim_reserved; /* Reserved */ uint16_t pim_cksum; /* IP-style checksum */ }; /* KAME-related name backward compatibility */ #define pim_ver pim_vers #define pim_rsv pim_reserved #ifdef _PIM_VT #define PIM_MAKE_VT(v, t) (0xff & (((v) << 4) | (0x0f & (t)))) #define PIM_VT_V(x) (((x) >> 4) & 0x0f) #define PIM_VT_T(x) ((x) & 0x0f) #endif /* _PIM_VT */ #define PIM_VERSION 2 #define PIM_MINLEN 8 /* PIM message min. length */ #define PIM_REG_MINLEN (PIM_MINLEN+20) /* PIM Register hdr + inner IPv4 hdr */ #define PIM6_REG_MINLEN (PIM_MINLEN+40) /* PIM Register hdr + inner IPv6 hdr */ /* * PIM message types */ #define PIM_HELLO 0x0 /* PIM-SM and PIM-DM */ #define PIM_REGISTER 0x1 /* PIM-SM only */ #define PIM_REGISTER_STOP 0x2 /* PIM-SM only */ #define PIM_JOIN_PRUNE 0x3 /* PIM-SM and PIM-DM */ #define PIM_BOOTSTRAP 0x4 /* PIM-SM only */ #define PIM_ASSERT 0x5 /* PIM-SM and PIM-DM */ #define PIM_GRAFT 0x6 /* PIM-DM only */ #define PIM_GRAFT_ACK 0x7 /* PIM-DM only */ #define PIM_CAND_RP_ADV 0x8 /* PIM-SM only */ #define PIM_ALL_DF_ELECTION 0xa /* Bidir-PIM-SM only */ /* * PIM-Register message flags */ #define PIM_BORDER_REGISTER 0x80000000U /* The Border bit (host-order) */ #define PIM_NULL_REGISTER 0x40000000U /* The Null-Register bit (host-order)*/ /* * All-PIM-Routers IPv4 and IPv6 multicast addresses */ #define INADDR_ALLPIM_ROUTERS_GROUP (uint32_t)0xe000000dU /* 224.0.0.13 */ #define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS "ff02::d" #define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d }}} #endif /* _NETINET_PIM_H_ */ pimd-2.3.2/include/netinet/pim_var.h000066400000000000000000000077301267035112600173600ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETINET_PIM_VAR_H_ #define _NETINET_PIM_VAR_H_ /* * Protocol Independent Multicast (PIM), * kernel variables and implementation-specific definitions. * * Written by George Edmond Eddy (Rusty), ISI, February 1998. * Modified by Pavlin Radoslavov, USC/ISI, May 1998, Aug 1999, October 2000. * Modified by Hitoshi Asaeda, WIDE, August 1998. */ /* * PIM statistics kept in the kernel */ struct pimstat { u_quad_t pims_rcv_total_msgs; /* total PIM messages received */ u_quad_t pims_rcv_total_bytes; /* total PIM bytes received */ u_quad_t pims_rcv_tooshort; /* rcvd with too few bytes */ u_quad_t pims_rcv_badsum; /* rcvd with bad checksum */ u_quad_t pims_rcv_badversion; /* rcvd bad PIM version */ u_quad_t pims_rcv_registers_msgs; /* rcvd regs. msgs (data only) */ u_quad_t pims_rcv_registers_bytes; /* rcvd regs. bytes (data only) */ u_quad_t pims_rcv_registers_wrongiif; /* rcvd regs. on wrong iif */ u_quad_t pims_rcv_badregisters; /* rcvd invalid registers */ u_quad_t pims_snd_registers_msgs; /* sent regs. msgs (data only) */ u_quad_t pims_snd_registers_bytes; /* sent regs. bytes (data only) */ }; #if (defined(KERNEL)) || (defined(_KERNEL)) extern struct pimstat pimstat; #if defined(NetBSD) || defined(__OpenBSD__) void pim_input(struct mbuf *, ...); #elif (defined(__FreeBSD__) && (__FreeBSD_version >= 400000)) || defined(__FreeBSD_kernel__) void pim_input(struct mbuf *, int, int); #else void pim_input(struct mbuf *, int); #endif /* ! (NetBSD || OpenBSD) */ #endif /* KERNEL */ /* * Names for PIM sysctl objects */ #if (defined(__FreeBSD__)) || (defined(NetBSD)) || defined(__OpenBSD__) || (defined(__bsdi__)) #define PIMCTL_STATS 1 /* statistics (read-only) */ #define PIMCTL_MAXID 2 #define PIMCTL_NAMES { \ { 0, 0 }, \ { "stats", CTLTYPE_STRUCT }, \ } #endif /* FreeBSD || NetBSD || OpenBSD || bsdi */ #if (defined(__FreeBSD__) && (__FreeBSD_version >= 400000)) || defined(__FreeBSD_kernel__) #if ((defined(KERNEL)) || (defined(_KERNEL))) #if defined(SYSCTL_DECL) SYSCTL_DECL(_net_inet_pim); #endif /* SYSCTL_DECL */ #endif /* KERNEL */ #endif /* FreeBSD >= 4.0 */ #if (defined(NetBSD) || defined(__OpenBSD__) || defined(__bsdi__)) #define PIMCTL_VARS { \ 0, \ 0, \ } int pim_sysctl(int *, u_int, void *, size_t *, void *, size_t); #endif /* NetBSD || OpenBSD || bsdi */ #endif /* _NETINET_PIM_VAR_H_ */ pimd-2.3.2/include/openbsd/000077500000000000000000000000001267035112600155275ustar00rootroot00000000000000pimd-2.3.2/include/openbsd/netinet/000077500000000000000000000000001267035112600171755ustar00rootroot00000000000000pimd-2.3.2/include/openbsd/netinet/ip_mroute.h000066400000000000000000000304161267035112600213550ustar00rootroot00000000000000/* $OpenBSD: ip_mroute.h,v 1.15 2009/07/13 19:14:29 michele Exp $ */ /* $NetBSD: ip_mroute.h,v 1.23 2004/04/21 17:49:46 itojun Exp $ */ #ifndef _NETINET_IP_MROUTE_H_ #define _NETINET_IP_MROUTE_H_ /* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, SGI, June 1996. * Modified by Pavlin Radoslavov, ICSI, October 2002. * * MROUTING Revision: 1.2 * and PIM-SMv2 and PIM-DM support, advanced API support, * bandwidth metering and signaling. */ #include #include /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert processing */ #define MRT_PIM MRT_ASSERT /* enable PIM processing */ #define MRT_API_SUPPORT 109 /* supported MRT API */ #define MRT_API_CONFIG 110 /* config MRT API */ #define MRT_ADD_BW_UPCALL 111 /* create bandwidth monitor */ #define MRT_DEL_BW_UPCALL 112 /* delete bandwidth monitor */ /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_int32_t vifbitmap_t; typedef u_int16_t vifi_t; /* type of a vif index */ #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_SETALL(m) ((m) = 0xffffffff) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */ #define VIFF_REGISTER 0x4 /* used for PIM Register encap/decap */ /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_int8_t vifc_flags; /* VIFF_ flags defined above */ u_int8_t vifc_threshold; /* min ttl required to forward on vif */ u_int32_t vifc_rate_limit; /* ignored */ struct in_addr vifc_lcl_addr;/* local interface address */ struct in_addr vifc_rmt_addr;/* remote address (tunnels only) */ }; /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC. * XXX if you change this, make sure to change struct mfcctl2 as well. */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated */ vifi_t mfcc_parent; /* incoming vif */ u_int8_t mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ }; /* * The new argument structure for MRT_ADD_MFC and MRT_DEL_MFC overlays * and extends the old struct mfcctl. */ struct mfcctl2 { /* the mfcctl fields */ struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_int8_t mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ /* extension fields */ u_int8_t mfcc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfcc_rp; /* the RP address */ }; /* * The advanced-API flags. * * The MRT_MFC_FLAGS_XXX API flags are also used as flags * for the mfcc_flags field. */ #define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* disable WRONGVIF signals */ #define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* border vif */ #define MRT_MFC_RP (1 << 8) /* enable RP address */ #define MRT_MFC_BW_UPCALL (1 << 9) /* enable bw upcalls */ #define MRT_MFC_FLAGS_ALL (MRT_MFC_FLAGS_DISABLE_WRONGVIF | \ MRT_MFC_FLAGS_BORDER_VIF) #define MRT_API_FLAGS_ALL (MRT_MFC_FLAGS_ALL | \ MRT_MFC_RP | \ MRT_MFC_BW_UPCALL) /* * Structure for installing or delivering an upcall if the * measured bandwidth is above or below a threshold. * * User programs (e.g. daemons) may have a need to know when the * bandwidth used by some data flow is above or below some threshold. * This interface allows the userland to specify the threshold (in * bytes and/or packets) and the measurement interval. Flows are * all packet with the same source and destination IP address. * At the moment the code is only used for multicast destinations * but there is nothing that prevents its use for unicast. * * The measurement interval cannot be shorter than some Tmin (currently, 3s). * The threshold is set in packets and/or bytes per_interval. * * Measurement works as follows: * * For >= measurements: * The first packet marks the start of a measurement interval. * During an interval we count packets and bytes, and when we * pass the threshold we deliver an upcall and we are done. * The first packet after the end of the interval resets the * count and restarts the measurement. * * For <= measurement: * We start a timer to fire at the end of the interval, and * then for each incoming packet we count packets and bytes. * When the timer fires, we compare the value with the threshold, * schedule an upcall if we are below, and restart the measurement * (reschedule timer and zero counters). */ struct bw_data { struct timeval b_time; u_int64_t b_packets; u_int64_t b_bytes; }; struct bw_upcall { struct in_addr bu_src; /* source address */ struct in_addr bu_dst; /* destination address */ u_int32_t bu_flags; /* misc flags (see below) */ #define BW_UPCALL_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_UPCALL_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_UPCALL_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_UPCALL_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_UPCALL_DELETE_ALL (1 << 4) /* delete all upcalls for s,d*/ struct bw_data bu_threshold; /* the bw threshold */ struct bw_data bu_measured; /* the measured bw */ }; /* max. number of upcalls to deliver together */ #define BW_UPCALLS_MAX 128 /* min. threshold time interval for bandwidth measurement */ #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC 3 #define BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC 0 /* * Argument structure used by mrouted to get src-grp pkt counts. */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts. */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* input packet count on vif */ u_long ocount; /* output packet count on vif */ u_long ibytes; /* input byte count on vif */ u_long obytes; /* output byte count on vif */ }; /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to mrouted */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; #ifdef _KERNEL /* * The kernel's virtual-interface structure. */ struct vif { u_int8_t v_flags; /* VIFF_ flags defined above */ u_int8_t v_threshold; /* min ttl required to forward on vif */ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* cached route if this is a tunnel */ struct timeout v_repq_ch; /* for tbf_reprocess_q() */ #ifdef RSVP_ISI int v_rsvp_on; /* # RSVP listening on this vif */ struct socket *v_rsvpd; /* # RSVPD daemon */ #endif /* RSVP_ISI */ }; /* * The kernel's multicast forwarding cache entry structure. * (A field for the type of service (mfc_tos) is to be added * at a future point.) */ struct mfc { LIST_ENTRY(mfc) mfc_hash; struct in_addr mfc_origin; /* ip origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated */ vifi_t mfc_parent; /* incoming vif */ u_int8_t mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert */ struct rtdetq *mfc_stall; /* pkts waiting for route */ u_int8_t mfc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ struct in_addr mfc_rp; /* the RP address */ struct bw_meter *mfc_bw_meter; /* list of bandwidth meters */ }; /* * Structure used to communicate from kernel to multicast router. * (Note the convenient similarity to an IP packet.) */ struct igmpmsg { u_int32_t unused1; u_int32_t unused2; u_int8_t im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */ #define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */ #define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */ #define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */ u_int8_t im_mbz; /* must be zero */ u_int8_t im_vif; /* vif rec'd on */ u_int8_t unused3; struct in_addr im_src, im_dst; }; /* * Argument structure used for pkt info. while upcall is made. */ struct rtdetq { struct mbuf *m; /* a copy of the packet */ struct ifnet *ifp; /* interface pkt came in on */ #ifdef UPCALL_TIMING struct timeval t; /* timestamp */ #endif /* UPCALL_TIMING */ struct rtdetq *next; }; #define MFCTBLSIZ 256 #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ /* * Structure for measuring the bandwidth and sending an upcall if the * measured bandwidth is above or below a threshold. */ struct bw_meter { struct bw_meter *bm_mfc_next; /* next bw meter (same mfc) */ struct bw_meter *bm_time_next; /* next bw meter (same time) */ uint32_t bm_time_hash; /* the time hash value */ struct mfc *bm_mfc; /* the corresponding mfc */ uint32_t bm_flags; /* misc flags (see below) */ #define BW_METER_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ #define BW_METER_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ #define BW_METER_GEQ (1 << 2) /* upcall if bw >= threshold */ #define BW_METER_LEQ (1 << 3) /* upcall if bw <= threshold */ #define BW_METER_USER_FLAGS (BW_METER_UNIT_PACKETS | \ BW_METER_UNIT_BYTES | \ BW_METER_GEQ | \ BW_METER_LEQ) #define BW_METER_UPCALL_DELIVERED (1 << 24) /* upcall was delivered */ struct bw_data bm_threshold; /* the upcall threshold */ struct bw_data bm_measured; /* the measured bw */ struct timeval bm_start_time; /* abs. time */ }; int ip_mrouter_set(struct socket *, int, struct mbuf **); int ip_mrouter_get(struct socket *, int, struct mbuf **); int mrt_ioctl(struct socket *, u_long, caddr_t); int ip_mrouter_done(void); void ip_mrouter_detach(struct ifnet *); void reset_vif(struct vif *); void vif_delete(struct ifnet *); #ifdef RSVP_ISI int ip_mforward(struct mbuf *, struct ifnet *, struct ip_moptions *); int legal_vif_num(int); int ip_rsvp_vif_init(struct socket *, struct mbuf *); int ip_rsvp_vif_done(struct socket *, struct mbuf *); void ip_rsvp_force_done(struct socket *); void rsvp_input(struct mbuf *, int, int); #else int ip_mforward(struct mbuf *, struct ifnet *); #endif /* RSVP_ISI */ #endif /* _KERNEL */ #endif /* _NETINET_IP_MROUTE_H_ */ pimd-2.3.2/include/sunos-cc/000077500000000000000000000000001267035112600156275ustar00rootroot00000000000000pimd-2.3.2/include/sunos-cc/netinet/000077500000000000000000000000001267035112600172755ustar00rootroot00000000000000pimd-2.3.2/include/sunos-cc/netinet/igmp.h000066400000000000000000000037061267035112600204100ustar00rootroot00000000000000/* * Internet Group Management Protocol (IGMP) definitions. * * Written by Steve Deering, Stanford, May 1988. * * MULTICAST $Revision: 3.5 $ */ /* * IGMP packet format. */ struct igmp { u_char igmp_type; /* version & type of IGMP message */ u_char igmp_code; /* code for routing sub-msgs */ u_short igmp_cksum; /* IP-style checksum */ struct in_addr igmp_group; /* group address being reported */ }; /* (zero for queries) */ #define IGMP_MINLEN 8 /* * Message types, including version number. */ #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* Host membership query */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Old membership report */ #define IGMP_DVMRP 0x13 /* DVMRP routing message */ #define IGMP_PIM 0x14 /* PIM routing message */ #define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16 /* New membership report */ #define IGMP_HOST_LEAVE_MESSAGE 0x17 /* Leave-group message */ #define IGMP_MTRACE_RESP 0x1e /* traceroute resp. (to sender) */ #define IGMP_MTRACE 0x1f /* mcast traceroute messages */ #define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */ /* query (in seconds) */ #define IGMP_TIMER_SCALE 10 /* denotes that the igmp->timer filed */ /* specifies time in 10th of seconds */ /* * States for the IGMPv2 state table */ #define IGMP_DELAYING_MEMBER 1 #define IGMP_IDLE_MEMBER 2 #define IGMP_LAZY_MEMBER 3 #define IGMP_SLEEPING_MEMBER 4 #define IGMP_AWAKENING_MEMBER 5 /* * We must remember whether the querier is an old or a new router. */ #define IGMP_OLD_ROUTER 0 #define IGMP_NEW_ROUTER 1 /* * Revert to new router if we haven't heard from an old router in * this amount of time. */ #define IGMP_AGE_THRESHOLD 540 pimd-2.3.2/include/sunos-cc/netinet/in.h000066400000000000000000000156251267035112600200650ustar00rootroot00000000000000/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */ /* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of California at Berkeley. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981. */ #ifndef _netinet_in_h #define _netinet_in_h /* * Protocols */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_IGMP 2 /* group control protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPIP 4 /* IP inside IP */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_HELLO 63 /* "hello" routing protocol */ #define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */ #define IPPROTO_RSVP 46 /* resource reservation proto*/ #define IPPROTO_PIM 103 /* Protocol Independent Mcast*/ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* * Port/socket numbers: network standard functions */ #define IPPORT_ECHO 7 #define IPPORT_DISCARD 9 #define IPPORT_SYSTAT 11 #define IPPORT_DAYTIME 13 #define IPPORT_NETSTAT 15 #define IPPORT_FTP 21 #define IPPORT_TELNET 23 #define IPPORT_SMTP 25 #define IPPORT_TIMESERVER 37 #define IPPORT_NAMESERVER 42 #define IPPORT_WHOIS 43 #define IPPORT_MTP 57 /* * Port/socket numbers: host specific functions */ #define IPPORT_TFTP 69 #define IPPORT_RJE 77 #define IPPORT_FINGER 79 #define IPPORT_TTYLINK 87 #define IPPORT_SUPDUP 95 /* * UNIX TCP sockets */ #define IPPORT_EXECSERVER 512 #define IPPORT_LOGINSERVER 513 #define IPPORT_CMDSERVER 514 #define IPPORT_EFSSERVER 520 /* * UNIX UDP sockets */ #define IPPORT_BIFFUDP 512 #define IPPORT_WHOSERVER 513 #define IPPORT_ROUTESERVER 520 /* 520+1 also used */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). * Ports > IPPORT_USERRESERVED are reserved * for servers, not necessarily privileged. */ #define IPPORT_RESERVED 1024 #define IPPORT_USERRESERVED 5000 /* * Link numbers */ #define IMPLINK_IP 155 #define IMPLINK_LOWEXPER 156 #define IMPLINK_HIGHEXPER 158 /* * Internet address * This definition contains obsolete fields for compatibility * with SunOS 3.x and 4.2bsd. The presence of subnets renders * divisions into fixed fields misleading at best. New code * should use only the s_addr field. */ struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define s_addr S_un.S_addr /* should be used for all code */ #define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */ #define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */ #define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */ #define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */ #define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */ }; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) #define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ #define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ #define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000) #define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000) #define INADDR_ANY (u_long)0x00000000 #define INADDR_LOOPBACK (u_long)0x7F000001 #define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */ #define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */ #define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Define a macro to stuff the loopback address into an Internet address */ #define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \ (a)->sin_family = AF_INET;} /* * Socket address, internet style. */ struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; /* * Options for use with [gs]etsockopt at the IP level. */ #define IP_OPTIONS 1 /* set/get IP per-packet options */ #define IP_MULTICAST_IF 2 /* set/get IP multicast interface */ #define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */ #define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */ #define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */ #define IP_MULTICAST_VIF 7 /* set/get IP mcast vir. interface */ #define IP_RSVP_ON 8 /* set rsvp var. in kernel */ #define IP_RSVP_OFF 9 /* unset rsvp var in kernel */ #define IP_RSVP_VIF_ON 10 /* set rsvp per-vif socket */ #define IP_RSVP_VIF_OFF 11 /* unset rsvp per-vif socket */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; #if !defined(vax) && !defined(ntohl) && !defined(i386) /* * Macros for number representation conversion. */ #define ntohl(x) (x) #define ntohs(x) (x) #define htonl(x) (x) #define htons(x) (x) #endif #if !defined(ntohl) && (defined(vax) || defined(i386)) u_short ntohs(), htons(); u_long ntohl(), htonl(); #endif #ifdef KERNEL extern struct domain inetdomain; extern struct protosw inetsw[]; struct in_addr in_makeaddr(); u_long in_netof(), in_lnaof(); #endif #endif /*!_netinet_in_h*/ pimd-2.3.2/include/sunos-cc/netinet/ip_mroute.h000066400000000000000000000204671267035112600214620ustar00rootroot00000000000000/* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, USC, September 1996. * * MROUTING $Revision: 3.5 $ */ /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert (wrong iif) processing */ #if BSD >= 199103 #define GET_TIME(t) microtime(&t) #elif defined(sun) #define GET_TIME(t) uniqtime(&t) #else #define GET_TIME(t) ((t) = time) #endif /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_long vifbitmap_t; typedef u_short vifi_t; /* type of a vif index */ #define ALL_VIFS (vifi_t)-1 #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_char vifc_flags; /* VIFF_ flags defined below */ u_char vifc_threshold; /* min ttl required to forward on vif*/ u_int vifc_rate_limit; /* max rate */ struct in_addr vifc_lcl_addr; /* local interface address */ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */ }; #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */ #define VIFF_REGISTER 0x4 /* vif used for register en/decap */ #ifdef PIM_REG_KERNEL_ENCAP #define VIFF_REGISTER_KERNEL_ENCAP 0x8 /* vif register with kernel encap */ #endif /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC * (mfcc_tos to be added at a future point) */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfcc_rp_addr; /* The RP address for encap. */ #endif }; /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to mrouted */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; /* * Argument structure used by mrouted to get src-grp pkt counts */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* Input packet count on vif */ u_long ocount; /* Output packet count on vif */ u_long ibytes; /* Input byte count on vif */ u_long obytes; /* Output byte count on vif */ }; #ifdef KERNEL /* * Argument structure used by PIM to get the RPF neighbor and IIF info * for a given source, from the unicast routing tables. * Maybe be substituted by routing sockets for the supporting systems */ struct rpfctl { struct in_addr source; /* the source for which we want iif and rpfnbr */ struct in_addr rpfneighbor;/* next hop towards the source */ vifi_t iif;/* the incoming interface to reach the next hop */ }; /* * The kernel's virtual-interface structure. */ struct vif { u_char v_flags; /* VIFF_ flags defined above */ u_char v_threshold; /* min ttl required to forward on vif*/ u_int v_rate_limit; /* max rate */ struct tbf *v_tbf; /* token bucket structure at intf. */ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* Cached route if this is a tunnel */ #ifdef RSVP_ISI u_int v_rsvp_on; /* # RSVP listening on this vif */ struct socket *v_rsvpd; /* # RSVPD daemon */ #endif /* RSVP_ISI */ }; /* * The kernel's multicast forwarding cache entry structure * (A field for the type of service (mfc_tos) is to be added * at a future point) */ struct mfc { struct in_addr mfc_origin; /* ip origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated*/ vifi_t mfc_parent; /* incoming vif */ u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert*/ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfc_rp_addr; /* The RP address for encap. */ #endif }; #endif /* KERNEL */ /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IP packet */ struct igmpmsg { u_long unused1; u_long unused2; u_char im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 #define IGMPMSG_WRONGVIF 2 #define IGMPMSG_WHOLEPKT 3 /* used for user level encap */ u_char im_mbz; /* must be zero */ u_char im_vif; /* vif rec'd on */ u_char unused3; struct in_addr im_src, im_dst; }; #ifdef KERNEL /* * Argument structure used for pkt info. while upcall is made */ struct rtdetq { struct mbuf *m; /* A copy of the packet */ struct ifnet *ifp; /* Interface pkt came in on */ #ifdef UPCALL_TIMING struct timeval t; /* Timestamp */ #endif /* UPCALL_TIMING */ }; #define MFCTBLSIZ 256 #if (MFCTBLSIZ & (MFCTBLSIZ - 1)) == 0 /* from sys:route.h */ #define MFCHASHMOD(h) ((h) & (MFCTBLSIZ - 1)) #else #define MFCHASHMOD(h) ((h) % MFCTBLSIZ) #endif #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ /* * Token Bucket filter code */ #define MAX_BKT_SIZE 10000 /* 10K bytes size */ #define MAXQSIZE 10 /* max # of pkts in queue */ /* * queue structure at each vif */ struct pkt_queue { u_long pkt_len; /* length of packet in queue */ struct mbuf *pkt_m; /* pointer to packet mbuf */ struct ip *pkt_ip; /* pointer to ip header */ }; /* * the token bucket filter at each vif */ struct tbf { u_long last_pkt_t; /* arr. time of last pkt */ u_long n_tok; /* no of tokens in bucket */ u_long q_len; /* length of queue at this vif */ }; #endif /* KERNEL */ pimd-2.3.2/include/sunos-cc/sys/000077500000000000000000000000001267035112600164455ustar00rootroot00000000000000pimd-2.3.2/include/sunos-cc/sys/sockio.h000066400000000000000000000105001267035112600201010ustar00rootroot00000000000000/* @(#)sockio.h 1.7 88/12/06 SMI; from UCB ioctl.h 7.1 6/4/86 */ /* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * General socket ioctl definitions. */ #ifndef _sys_sockio_h #define _sys_sockio_h #include /* socket i/o controls */ #define SIOCSHIWAT _IOW(s, 0, int) /* set high watermark */ #define SIOCGHIWAT _IOR(s, 1, int) /* get high watermark */ #define SIOCSLOWAT _IOW(s, 2, int) /* set low watermark */ #define SIOCGLOWAT _IOR(s, 3, int) /* get low watermark */ #define SIOCATMARK _IOR(s, 7, int) /* at oob mark? */ #define SIOCSPGRP _IOW(s, 8, int) /* set process group */ #define SIOCGPGRP _IOR(s, 9, int) /* get process group */ #define SIOCADDRT _IOW(r, 10, struct rtentry) /* add route */ #define SIOCDELRT _IOW(r, 11, struct rtentry) /* delete route */ #define SIOCSETRTINFO _IOWR(r, 12, struct fullrtentry) /* change aux info */ #define SIOCGETRTINFO _IOWR(r, 13, struct fullrtentry) /* read aux info */ #define SIOCGETVIFCNT _IOWR(r, 20, struct sioc_vif_req)/* get vif pkt cnt */ #define SIOCGETSGCNT _IOWR(r, 21, struct sioc_sg_req) /* get s,g pkt cnt */ /* AH 96/9/23 added GETRPF for PIM support */ #define SIOCGETRPF _IOWR(r, 22, struct rpfctl) /* get rpf info */ #ifdef RSVP_ISI #define SIOCGETVIFINF _IOWR(r, 15, struct vif_conf) /* read m/c vifs */ #endif RSVP_ISI #define SIOCSIFADDR _IOW(i, 12, struct ifreq) /* set ifnet address */ #define SIOCGIFADDR _IOWR(i,13, struct ifreq) /* get ifnet address */ #define SIOCSIFDSTADDR _IOW(i, 14, struct ifreq) /* set p-p address */ #define SIOCGIFDSTADDR _IOWR(i,15, struct ifreq) /* get p-p address */ #define SIOCSIFFLAGS _IOW(i, 16, struct ifreq) /* set ifnet flags */ #define SIOCGIFFLAGS _IOWR(i,17, struct ifreq) /* get ifnet flags */ #define SIOCSIFMEM _IOW(i, 18, struct ifreq) /* set interface mem */ #define SIOCGIFMEM _IOWR(i,19, struct ifreq) /* get interface mem */ #define SIOCGIFCONF _IOWR(i,20, struct ifconf) /* get ifnet list */ #define SIOCSIFMTU _IOW(i, 21, struct ifreq) /* set if_mtu */ #define SIOCGIFMTU _IOWR(i,22, struct ifreq) /* get if_mtu */ /* from 4.3BSD */ #define SIOCGIFBRDADDR _IOWR(i,23, struct ifreq) /* get broadcast addr */ #define SIOCSIFBRDADDR _IOW(i,24, struct ifreq) /* set broadcast addr */ #define SIOCGIFNETMASK _IOWR(i,25, struct ifreq) /* get net addr mask */ #define SIOCSIFNETMASK _IOW(i,26, struct ifreq) /* set net addr mask */ #define SIOCGIFMETRIC _IOWR(i,27, struct ifreq) /* get IF metric */ #define SIOCSIFMETRIC _IOW(i,28, struct ifreq) /* set IF metric */ #define SIOCSARP _IOW(i, 30, struct arpreq) /* set arp entry */ #define SIOCGARP _IOWR(i,31, struct arpreq) /* get arp entry */ #define SIOCDARP _IOW(i, 32, struct arpreq) /* delete arp entry */ #define SIOCUPPER _IOW(i, 40, struct ifreq) /* attach upper layer */ #define SIOCLOWER _IOW(i, 41, struct ifreq) /* attach lower layer */ #define SIOCSETSYNC _IOW(i, 44, struct ifreq) /* set syncmode */ #define SIOCGETSYNC _IOWR(i, 45, struct ifreq) /* get syncmode */ #define SIOCSSDSTATS _IOWR(i, 46, struct ifreq) /* sync data stats */ #define SIOCSSESTATS _IOWR(i, 47, struct ifreq) /* sync error stats */ #define SIOCSPROMISC _IOW(i, 48, int) /* request promisc mode on/off */ #define SIOCADDMULTI _IOW(i, 49, struct ifreq) /* set m/c address */ #define SIOCDELMULTI _IOW(i, 50, struct ifreq) /* clr m/c address */ /* FDDI controls */ #define SIOCFDRESET _IOW(i, 51, struct ifreq) /* Reset FDDI */ #define SIOCFDSLEEP _IOW(i, 52, struct ifreq) /* Sleep until next dnld req */ #define SIOCSTRTFMWAR _IOW(i, 53, struct ifreq) /* Start FW at an addr */ #define SIOCLDNSTRTFW _IOW(i, 54, struct ifreq) /* Load the shared memory */ #define SIOCGETFDSTAT _IOW(i, 55, struct ifreq) /* Get FDDI stats */ #define SIOCFDNMIINT _IOW(i, 56, struct ifreq) /* NMI to fddi */ #define SIOCFDEXUSER _IOW(i, 57, struct ifreq) /* Exec in user mode */ #define SIOCFDGNETMAP _IOW(i, 58, struct ifreq) /* Get a netmap entry */ #define SIOCFDGIOCTL _IOW(i, 59, struct ifreq) /* Generic ioctl for fddi */ /* protocol i/o controls */ #define SIOCSNIT _IOW(p, 0, struct nit_ioc) /* set nit modes */ #define SIOCGNIT _IOWR(p, 1, struct nit_ioc) /* get nit modes */ #endif /*!_sys_sockio_h*/ pimd-2.3.2/include/sunos-gcc/000077500000000000000000000000001267035112600157765ustar00rootroot00000000000000pimd-2.3.2/include/sunos-gcc/netinet/000077500000000000000000000000001267035112600174445ustar00rootroot00000000000000pimd-2.3.2/include/sunos-gcc/netinet/igmp.h000066400000000000000000000037061267035112600205570ustar00rootroot00000000000000/* * Internet Group Management Protocol (IGMP) definitions. * * Written by Steve Deering, Stanford, May 1988. * * MULTICAST $Revision: 3.5 $ */ /* * IGMP packet format. */ struct igmp { u_char igmp_type; /* version & type of IGMP message */ u_char igmp_code; /* code for routing sub-msgs */ u_short igmp_cksum; /* IP-style checksum */ struct in_addr igmp_group; /* group address being reported */ }; /* (zero for queries) */ #define IGMP_MINLEN 8 /* * Message types, including version number. */ #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* Host membership query */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Old membership report */ #define IGMP_DVMRP 0x13 /* DVMRP routing message */ #define IGMP_PIM 0x14 /* PIM routing message */ #define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16 /* New membership report */ #define IGMP_HOST_LEAVE_MESSAGE 0x17 /* Leave-group message */ #define IGMP_MTRACE_RESP 0x1e /* traceroute resp. (to sender) */ #define IGMP_MTRACE 0x1f /* mcast traceroute messages */ #define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */ /* query (in seconds) */ #define IGMP_TIMER_SCALE 10 /* denotes that the igmp->timer filed */ /* specifies time in 10th of seconds */ /* * States for the IGMPv2 state table */ #define IGMP_DELAYING_MEMBER 1 #define IGMP_IDLE_MEMBER 2 #define IGMP_LAZY_MEMBER 3 #define IGMP_SLEEPING_MEMBER 4 #define IGMP_AWAKENING_MEMBER 5 /* * We must remember whether the querier is an old or a new router. */ #define IGMP_OLD_ROUTER 0 #define IGMP_NEW_ROUTER 1 /* * Revert to new router if we haven't heard from an old router in * this amount of time. */ #define IGMP_AGE_THRESHOLD 540 pimd-2.3.2/include/sunos-gcc/netinet/in.h000066400000000000000000000156451267035112600202360ustar00rootroot00000000000000/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */ /* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of California at Berkeley. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981. */ #ifndef _netinet_in_h #define _netinet_in_h /* * Protocols */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_IGMP 2 /* group control protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPIP 4 /* IP inside IP */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_HELLO 63 /* "hello" routing protocol */ #define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */ #define IPPROTO_RSVP 46 /* resource reservation proto*/ #define IPPROTO_PIM 103 /* Protocol Independent Mcast*/ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* * Port/socket numbers: network standard functions */ #define IPPORT_ECHO 7 #define IPPORT_DISCARD 9 #define IPPORT_SYSTAT 11 #define IPPORT_DAYTIME 13 #define IPPORT_NETSTAT 15 #define IPPORT_FTP 21 #define IPPORT_TELNET 23 #define IPPORT_SMTP 25 #define IPPORT_TIMESERVER 37 #define IPPORT_NAMESERVER 42 #define IPPORT_WHOIS 43 #define IPPORT_MTP 57 /* * Port/socket numbers: host specific functions */ #define IPPORT_TFTP 69 #define IPPORT_RJE 77 #define IPPORT_FINGER 79 #define IPPORT_TTYLINK 87 #define IPPORT_SUPDUP 95 /* * UNIX TCP sockets */ #define IPPORT_EXECSERVER 512 #define IPPORT_LOGINSERVER 513 #define IPPORT_CMDSERVER 514 #define IPPORT_EFSSERVER 520 /* * UNIX UDP sockets */ #define IPPORT_BIFFUDP 512 #define IPPORT_WHOSERVER 513 #define IPPORT_ROUTESERVER 520 /* 520+1 also used */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). * Ports > IPPORT_USERRESERVED are reserved * for servers, not necessarily privileged. */ #define IPPORT_RESERVED 1024 #define IPPORT_USERRESERVED 5000 /* * Link numbers */ #define IMPLINK_IP 155 #define IMPLINK_LOWEXPER 156 #define IMPLINK_HIGHEXPER 158 /* * Internet address * This definition contains obsolete fields for compatibility * with SunOS 3.x and 4.2bsd. The presence of subnets renders * divisions into fixed fields misleading at best. New code * should use only the s_addr field. */ struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define s_addr S_un.S_addr /* should be used for all code */ #define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */ #define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */ #define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */ #define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */ #define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */ }; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) #define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ #define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ #define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ #define IN_MULTICAST(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000) #define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000) #define INADDR_ANY (u_long)0x00000000 #define INADDR_LOOPBACK (u_long)0x7F000001 #define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */ #define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */ #define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Define a macro to stuff the loopback address into an Internet address */ #define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \ (a)->sin_family = AF_INET;} /* * Socket address, internet style. */ struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; /* * Options for use with [gs]etsockopt at the IP level. */ #define IP_OPTIONS 1 /* set/get IP per-packet options */ #define IP_MULTICAST_IF 2 /* set/get IP multicast interface */ #define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */ #define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */ #define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */ #define IP_MULTICAST_VIF 7 /* set/get IP mcast vir. interface */ #define IP_RSVP_ON 8 /* set rsvp var. in kernel */ #define IP_RSVP_OFF 9 /* unset rsvp var in kernel */ #define IP_RSVP_VIF_ON 10 /* set rsvp per-vif socket */ #define IP_RSVP_VIF_OFF 11 /* unset rsvp per-vif socket */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; #if !defined(__vax__) && !defined(ntohl) && !defined(__i386__) /* * Macros for number representation conversion. */ #define ntohl(x) (x) #define ntohs(x) (x) #define htonl(x) (x) #define htons(x) (x) #endif #if !defined(ntohl) && (defined(__vax__) || defined(__i386__)) u_short ntohs(), htons(); u_long ntohl(), htonl(); #endif #ifdef KERNEL extern struct domain inetdomain; extern struct protosw inetsw[]; struct in_addr in_makeaddr(); u_long in_netof(), in_lnaof(); #endif #endif /*!_netinet_in_h*/ pimd-2.3.2/include/sunos-gcc/netinet/ip_mroute.h000066400000000000000000000204731267035112600216260ustar00rootroot00000000000000/* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, USC, September 1996. * * MROUTING $Revision: 3.5 $ */ /* * Multicast Routing set/getsockopt commands. */ #define MRT_INIT 100 /* initialize forwarder */ #define MRT_DONE 101 /* shut down forwarder */ #define MRT_ADD_VIF 102 /* create virtual interface */ #define MRT_DEL_VIF 103 /* delete virtual interface */ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ #define MRT_ASSERT 107 /* enable assert (wrong iif) processing */ #if BSD >= 199103 #define GET_TIME(t) microtime(&t) #elif defined(__sun__) #define GET_TIME(t) uniqtime(&t) #else #define GET_TIME(t) ((t) = time) #endif /* * Types and macros for handling bitmaps with one bit per virtual interface. */ #define MAXVIFS 32 typedef u_long vifbitmap_t; typedef u_short vifi_t; /* type of a vif index */ #define ALL_VIFS (vifi_t)-1 #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) /* * Argument structure for MRT_ADD_VIF. * (MRT_DEL_VIF takes a single vifi_t argument.) */ struct vifctl { vifi_t vifc_vifi; /* the index of the vif to be added */ u_char vifc_flags; /* VIFF_ flags defined below */ u_char vifc_threshold; /* min ttl required to forward on vif*/ u_int vifc_rate_limit; /* max rate */ struct in_addr vifc_lcl_addr; /* local interface address */ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */ }; #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP src routing */ #define VIFF_REGISTER 0x4 /* vif used for register en/decap */ #ifdef PIM_REG_KERNEL_ENCAP #define VIFF_REGISTER_KERNEL_ENCAP 0x8 /* vif register with kernel encap */ #endif /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC * (mfcc_tos to be added at a future point) */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ struct in_addr mfcc_mcastgrp; /* multicast group associated*/ vifi_t mfcc_parent; /* incoming vif */ u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfcc_rp_addr; /* The RP address for encap. */ #endif }; /* * The kernel's multicast routing statistics. */ struct mrtstat { u_long mrts_mfc_lookups; /* # forw. cache hash table hits */ u_long mrts_mfc_misses; /* # forw. cache hash table misses */ u_long mrts_upcalls; /* # calls to mrouted */ u_long mrts_no_route; /* no route for packet's origin */ u_long mrts_bad_tunnel; /* malformed tunnel options */ u_long mrts_cant_tunnel; /* no room for tunnel options */ u_long mrts_wrong_if; /* arrived on wrong interface */ u_long mrts_upq_ovflw; /* upcall Q overflow */ u_long mrts_cache_cleanups; /* # entries with no upcalls */ u_long mrts_drop_sel; /* pkts dropped selectively */ u_long mrts_q_overflow; /* pkts dropped - Q overflow */ u_long mrts_pkt2large; /* pkts dropped - size > BKT SIZE */ u_long mrts_upq_sockfull; /* upcalls dropped - socket full */ }; /* * Argument structure used by mrouted to get src-grp pkt counts */ struct sioc_sg_req { struct in_addr src; struct in_addr grp; u_long pktcnt; u_long bytecnt; u_long wrong_if; }; /* * Argument structure used by mrouted to get vif pkt counts */ struct sioc_vif_req { vifi_t vifi; /* vif number */ u_long icount; /* Input packet count on vif */ u_long ocount; /* Output packet count on vif */ u_long ibytes; /* Input byte count on vif */ u_long obytes; /* Output byte count on vif */ }; #ifdef KERNEL /* * Argument structure used by PIM to get the RPF neighbor and IIF info * for a given source, from the unicast routing tables. * Maybe be substituted by routing sockets for the supporting systems */ struct rpfctl { struct in_addr source; /* the source for which we want iif and rpfnbr */ struct in_addr rpfneighbor;/* next hop towards the source */ vifi_t iif;/* the incoming interface to reach the next hop */ }; /* * The kernel's virtual-interface structure. */ struct vif { u_char v_flags; /* VIFF_ flags defined above */ u_char v_threshold; /* min ttl required to forward on vif*/ u_int v_rate_limit; /* max rate */ struct tbf *v_tbf; /* token bucket structure at intf. */ struct in_addr v_lcl_addr; /* local interface address */ struct in_addr v_rmt_addr; /* remote address (tunnels only) */ struct ifnet *v_ifp; /* pointer to interface */ u_long v_pkt_in; /* # pkts in on interface */ u_long v_pkt_out; /* # pkts out on interface */ u_long v_bytes_in; /* # bytes in on interface */ u_long v_bytes_out; /* # bytes out on interface */ struct route v_route; /* Cached route if this is a tunnel */ #ifdef RSVP_ISI u_int v_rsvp_on; /* # RSVP listening on this vif */ struct socket *v_rsvpd; /* # RSVPD daemon */ #endif /* RSVP_ISI */ }; /* * The kernel's multicast forwarding cache entry structure * (A field for the type of service (mfc_tos) is to be added * at a future point) */ struct mfc { struct in_addr mfc_origin; /* ip origin of mcasts */ struct in_addr mfc_mcastgrp; /* multicast group associated*/ vifi_t mfc_parent; /* incoming vif */ u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ u_long mfc_pkt_cnt; /* pkt count for src-grp */ u_long mfc_byte_cnt; /* byte count for src-grp */ u_long mfc_wrong_if; /* wrong if for src-grp */ int mfc_expire; /* time to clean entry up */ struct timeval mfc_last_assert; /* last time I sent an assert*/ #ifdef PIM_REG_KERNEL_ENCAP struct in_addr mfc_rp_addr; /* The RP address for encap. */ #endif }; #endif /* KERNEL */ /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IP packet */ struct igmpmsg { u_long unused1; u_long unused2; u_char im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 #define IGMPMSG_WRONGVIF 2 #define IGMPMSG_WHOLEPKT 3 /* used for user level encap */ u_char im_mbz; /* must be zero */ u_char im_vif; /* vif rec'd on */ u_char unused3; struct in_addr im_src, im_dst; }; #ifdef KERNEL /* * Argument structure used for pkt info. while upcall is made */ struct rtdetq { struct mbuf *m; /* A copy of the packet */ struct ifnet *ifp; /* Interface pkt came in on */ #ifdef UPCALL_TIMING struct timeval t; /* Timestamp */ #endif /* UPCALL_TIMING */ }; #define MFCTBLSIZ 256 #if (MFCTBLSIZ & (MFCTBLSIZ - 1)) == 0 /* from sys:route.h */ #define MFCHASHMOD(h) ((h) & (MFCTBLSIZ - 1)) #else #define MFCHASHMOD(h) ((h) % MFCTBLSIZ) #endif #define MAX_UPQ 4 /* max. no of pkts in upcall Q */ /* * Token Bucket filter code */ #define MAX_BKT_SIZE 10000 /* 10K bytes size */ #define MAXQSIZE 10 /* max # of pkts in queue */ /* * queue structure at each vif */ struct pkt_queue { u_long pkt_len; /* length of packet in queue */ struct mbuf *pkt_m; /* pointer to packet mbuf */ struct ip *pkt_ip; /* pointer to ip header */ }; /* * the token bucket filter at each vif */ struct tbf { u_long last_pkt_t; /* arr. time of last pkt */ u_long n_tok; /* no of tokens in bucket */ u_long q_len; /* length of queue at this vif */ }; #endif /* KERNEL */ pimd-2.3.2/include/sunos-gcc/sys/000077500000000000000000000000001267035112600166145ustar00rootroot00000000000000pimd-2.3.2/include/sunos-gcc/sys/sockio.h000066400000000000000000000104241267035112600202550ustar00rootroot00000000000000/* @(#)sockio.h 1.7 88/12/06 SMI; from UCB ioctl.h 7.1 6/4/86 */ /* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * General socket ioctl definitions. */ #ifndef _sys_sockio_h #define _sys_sockio_h #include /* socket i/o controls */ #define SIOCSHIWAT _IOW('s', 0, int) /* set high watermark */ #define SIOCGHIWAT _IOR('s', 1, int) /* get high watermark */ #define SIOCSLOWAT _IOW('s', 2, int) /* set low watermark */ #define SIOCGLOWAT _IOR('s', 3, int) /* get low watermark */ #define SIOCATMARK _IOR('s', 7, int) /* at oob mark? */ #define SIOCSPGRP _IOW('s', 8, int) /* set process group */ #define SIOCGPGRP _IOR('s', 9, int) /* get process group */ #define SIOCADDRT _IOW('r', 10, struct rtentry) /* add route */ #define SIOCDELRT _IOW('r', 11, struct rtentry) /* delete route */ #define SIOCGETVIFCNT _IOWR('r', 20, struct sioc_vif_req)/* get vif pkt cnt */ #define SIOCGETSGCNT _IOWR('r', 21, struct sioc_sg_req) /* get s,g pkt cnt */ /* AH 96/9/23 added GETRPF for PIM support */ #define SIOCGETRPF _IOWR('r', 22, struct rpfctl) /* get rpf info */ #ifdef RSVP_ISI #define SIOCGETVIFINF _IOWR('r', 15, struct vif_conf) /* read m/c vifs */ #endif RSVP_ISI #define SIOCSIFADDR _IOW('i', 12, struct ifreq) /* set ifnet address */ #define SIOCGIFADDR _IOWR('i',13, struct ifreq) /* get ifnet address */ #define SIOCSIFDSTADDR _IOW('i', 14, struct ifreq) /* set p-p address */ #define SIOCGIFDSTADDR _IOWR('i',15, struct ifreq) /* get p-p address */ #define SIOCSIFFLAGS _IOW('i', 16, struct ifreq) /* set ifnet flags */ #define SIOCGIFFLAGS _IOWR('i',17, struct ifreq) /* get ifnet flags */ #define SIOCSIFMEM _IOW('i', 18, struct ifreq) /* set interface mem */ #define SIOCGIFMEM _IOWR('i',19, struct ifreq) /* get interface mem */ #define SIOCGIFCONF _IOWR('i',20, struct ifconf) /* get ifnet list */ #define SIOCSIFMTU _IOW('i', 21, struct ifreq) /* set if_mtu */ #define SIOCGIFMTU _IOWR('i',22, struct ifreq) /* get if_mtu */ /* from 4.3BSD */ #define SIOCGIFBRDADDR _IOWR('i',23, struct ifreq) /* get broadcast addr */ #define SIOCSIFBRDADDR _IOW('i',24, struct ifreq) /* set broadcast addr */ #define SIOCGIFNETMASK _IOWR('i',25, struct ifreq) /* get net addr mask */ #define SIOCSIFNETMASK _IOW('i',26, struct ifreq) /* set net addr mask */ #define SIOCGIFMETRIC _IOWR('i',27, struct ifreq) /* get IF metric */ #define SIOCSIFMETRIC _IOW('i',28, struct ifreq) /* set IF metric */ #define SIOCSARP _IOW('i', 30, struct arpreq) /* set arp entry */ #define SIOCGARP _IOWR('i',31, struct arpreq) /* get arp entry */ #define SIOCDARP _IOW('i', 32, struct arpreq) /* delete arp entry */ #define SIOCUPPER _IOW('i', 40, struct ifreq) /* attach upper layer */ #define SIOCLOWER _IOW('i', 41, struct ifreq) /* attach lower layer */ #define SIOCSETSYNC _IOW('i', 44, struct ifreq) /* set syncmode */ #define SIOCGETSYNC _IOWR('i', 45, struct ifreq) /* get syncmode */ #define SIOCSSDSTATS _IOWR('i', 46, struct ifreq) /* sync data stats */ #define SIOCSSESTATS _IOWR('i', 47, struct ifreq) /* sync error stats */ #define SIOCSPROMISC _IOW('i', 48, int) /* request promisc mode on/off */ #define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* set m/c address */ #define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* clr m/c address */ /* FDDI controls */ #define SIOCFDRESET _IOW('i', 51, struct ifreq) /* Reset FDDI */ #define SIOCFDSLEEP _IOW('i', 52, struct ifreq) /* Sleep until next dnld req */ #define SIOCSTRTFMWAR _IOW('i', 53, struct ifreq) /* Start FW at an addr */ #define SIOCLDNSTRTFW _IOW('i', 54, struct ifreq) /* Load the shared memory */ #define SIOCGETFDSTAT _IOW('i', 55, struct ifreq) /* Get FDDI stats */ #define SIOCFDNMIINT _IOW('i', 56, struct ifreq) /* NMI to fddi */ #define SIOCFDEXUSER _IOW('i', 57, struct ifreq) /* Exec in user mode */ #define SIOCFDGNETMAP _IOW('i', 58, struct ifreq) /* Get a netmap entry */ #define SIOCFDGIOCTL _IOW('i', 59, struct ifreq) /* Generic ioctl for fddi */ /* protocol i/o controls */ #define SIOCSNIT _IOW('p', 0, struct nit_ioc) /* set nit modes */ #define SIOCGNIT _IOWR('p', 1, struct nit_ioc) /* get nit modes */ #endif /*!_sys_sockio_h*/ pimd-2.3.2/inet.c000066400000000000000000000160211267035112600135550ustar00rootroot00000000000000/* * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". Use of the mrouted program represents acceptance of * the terms and conditions listed in that file. * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * * inet.c,v 3.8.4.1 1997/01/29 19:49:33 fenner Exp */ #define C(x) ((x) & 0xff) #include "defs.h" /* * Exported variables. */ char s1[MAX_INET_BUF_LEN]; /* buffers to hold the string representations */ char s2[MAX_INET_BUF_LEN]; /* of IP addresses, to be passed to inet_fmt() */ char s3[MAX_INET_BUF_LEN]; char s4[MAX_INET_BUF_LEN]; /* * Verify that a given IP address is credible as a host address. * (Without a mask, cannot detect addresses of the form {subnet,0} or * {subnet,-1}.) */ int inet_valid_host(uint32_t naddr) { uint32_t addr; addr = ntohl(naddr); return !(IN_MULTICAST(addr) || IN_BADCLASS (addr) || (addr & 0xff000000) == 0); } /* * Verify that a given netmask is plausible; * make sure that it is a series of 1's followed by * a series of 0's with no discontiguous 1's. */ int inet_valid_mask(uint32_t mask) { if (~(((mask & -mask) - 1) | mask) != 0) { /* Mask is not contiguous */ return FALSE; } return TRUE; } /* * Verify that a given subnet number and mask pair are credible. * * With CIDR, almost any subnet and mask are credible. mrouted still * can't handle aggregated class A's, so we still check that, but * otherwise the only requirements are that the subnet address is * within the [ABC] range and that the host bits of the subnet * are all 0. */ int inet_valid_subnet(uint32_t nsubnet, uint32_t nmask) { uint32_t subnet, mask; subnet = ntohl(nsubnet); mask = ntohl(nmask); if ((subnet & mask) != subnet) return FALSE; if (subnet == 0) return mask == 0; if (IN_CLASSA(subnet)) { if (mask < 0xff000000 || (subnet & 0xff000000) == 0x7f000000 || (subnet & 0xff000000) == 0x00000000) return FALSE; } else if (IN_CLASSD(subnet) || IN_BADCLASS(subnet)) { /* Above Class C address space */ return FALSE; } if (subnet & ~mask) { /* Host bits are set in the subnet */ return FALSE; } if (!inet_valid_mask(mask)) { /* Netmask is not contiguous */ return FALSE; } return TRUE; } /* * Convert an IP address in uint32_t (network) format into a printable string. */ char *inet_fmt(uint32_t addr, char *s, size_t len) { uint8_t *a; a = (uint8_t *)&addr; snprintf(s, len, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); return s; } /* * Convert the printable string representation of an IP address into the * uint32_t (network) format. Return 0xffffffff on error. (To detect the * legal address with that value, you must explicitly compare the string * with "255.255.255.255".) * The return value is in network order. */ uint32_t inet_parse(char *s, int n) { uint32_t a = 0; u_int a0 = 0, a1 = 0, a2 = 0, a3 = 0; int i; char c; i = sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c); if (i < n || i > 4 || a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255) return 0xffffffff; ((uint8_t *)&a)[0] = a0; ((uint8_t *)&a)[1] = a1; ((uint8_t *)&a)[2] = a2; ((uint8_t *)&a)[3] = a3; return a; } /* * inet_cksum extracted from: * P I N G . C * * Author - * Mike Muuss * U. S. Army Ballistic Research Laboratory * December, 1983 * Modified at Uc Berkeley * * (ping.c) Status - * Public Domain. Distribution Unlimited. * * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ int inet_cksum(uint16_t *addr, u_int len) { int sum = 0; int nleft = (int)len; uint16_t *w = addr; uint16_t answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(uint8_t *) (&answer) = *(uint8_t *)w ; sum += answer; } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return answer; } /* * Called by following netname() to create a mask specified network address. */ void trimdomain(char *cp) { static char domain[MAXHOSTNAMELEN + 1]; static int first = 1; char *s; if (first) { first = 0; if (gethostname(domain, MAXHOSTNAMELEN) == 0 && (s = strchr(domain, '.'))) (void) strlcpy(domain, s + 1, sizeof(domain)); else domain[0] = 0; } if (domain[0]) { while ((cp = strchr(cp, '.'))) { if (!strcasecmp(cp + 1, domain)) { *cp = 0; break; } cp++; } } } static uint32_t forgemask(uint32_t a) { uint32_t m; if (IN_CLASSA(a)) m = IN_CLASSA_NET; else if (IN_CLASSB(a)) m = IN_CLASSB_NET; else m = IN_CLASSC_NET; return (m); } static void domask(char *dst, size_t len, uint32_t addr, uint32_t mask) { int b, i; if (!mask || (forgemask(addr) == mask)) { *dst = '\0'; return; } i = 0; for (b = 0; b < 32; b++) { if (mask & (1 << b)) { int bb; i = b; for (bb = b+1; bb < 32; bb++) { if (!(mask & (1 << bb))) { i = -1; /* noncontig */ break; } } break; } } if (i == -1) snprintf(dst, len, "&0x%x", mask); else snprintf(dst, len, "/%d", 32 - i); } /* * Return the name of the network whose address is given. * The address is assumed to be that of a net or subnet, not a host. */ char *netname(uint32_t addr, uint32_t mask) { static char line[MAXHOSTNAMELEN + 4]; uint32_t omask; uint32_t i; i = ntohl(addr); omask = mask = ntohl(mask); if ((i & 0xffffff) == 0) snprintf(line, sizeof(line), "%u", C(i >> 24)); else if ((i & 0xffff) == 0) snprintf(line, sizeof(line), "%u.%u", C(i >> 24) , C(i >> 16)); else if ((i & 0xff) == 0) snprintf(line, sizeof(line), "%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8)); else snprintf(line, sizeof(line), "%u.%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8), C(i)); domask(line + strlen(line), sizeof(line) - strlen(line), i, omask); return line; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/kern.c000066400000000000000000000373631267035112600135710ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #ifdef RAW_OUTPUT_IS_RAW int curttl = 0; #endif /* * XXX: in Some BSD's there is only MRT_ASSERT, but in Linux there are * both MRT_ASSERT and MRT_PIM */ #ifndef MRT_PIM #define MRT_PIM MRT_ASSERT #endif #ifdef __linux__ /* Currently only available on Linux */ # ifndef MRT_TABLE # define MRT_TABLE (MRT_BASE + 9) # endif #endif /* * Open/init the multicast routing in the kernel and sets the * MRT_PIM (aka MRT_ASSERT) flag in the kernel. */ void k_init_pim(int socket) { int v = 1; #ifdef MRT_TABLE /* Currently only available on Linux */ if (mrt_table_id != 0) { logit(LOG_INFO, 0, "Initializing multicast routing table id %u", mrt_table_id); if (setsockopt(socket, IPPROTO_IP, MRT_TABLE, &mrt_table_id, sizeof(mrt_table_id)) < 0) { logit(LOG_WARNING, errno, "Cannot set multicast routing table id"); logit(LOG_ERR, 0, "Make sure your kernel has CONFIG_IP_MROUTE_MULTIPLE_TABLES=y"); } } #endif if (setsockopt(socket, IPPROTO_IP, MRT_INIT, (char *)&v, sizeof(int)) < 0) { if (errno == EADDRINUSE) logit(LOG_ERR, 0, "Another multicast routing application is already running."); else logit(LOG_ERR, errno, "Cannot enable multicast routing in kernel"); } if (setsockopt(socket, IPPROTO_IP, MRT_PIM, (char *)&v, sizeof(int)) < 0) logit(LOG_ERR, errno, "Cannot set PIM flag in kernel"); } /* * Stops the multicast routing in the kernel and resets the * MRT_PIM (aka MRT_ASSERT) flag in the kernel. */ void k_stop_pim(int socket) { int v = 0; if (setsockopt(socket, IPPROTO_IP, MRT_PIM, (char *)&v, sizeof(int)) < 0) logit(LOG_ERR, errno, "Cannot reset PIM flag in kernel"); if (setsockopt(socket, IPPROTO_IP, MRT_DONE, (char *)NULL, 0) < 0) logit(LOG_ERR, errno, "Cannot disable multicast routing in kernel"); } /* * Set the socket sending buffer. `bufsize` is the preferred size, * `minsize` is the smallest acceptable size. */ void k_set_sndbuf(int socket, int bufsize, int minsize) { int delta = bufsize / 2; int iter = 0; /* * Set the socket buffer. If we can't set it as large as we * want, search around to try to find the highest acceptable * value. The highest acceptable value being smaller than * minsize is a fatal error. */ if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { bufsize -= delta; while (1) { iter++; if (delta > 1) delta /= 2; if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { bufsize -= delta; } else { if (delta < 1024) break; bufsize += delta; } } if (bufsize < minsize) { logit(LOG_ERR, 0, "OS-allowed send buffer size %u < app min %u", bufsize, minsize); /*NOTREACHED*/ } } IF_DEBUG(DEBUG_KERN) { logit(LOG_DEBUG, 0, "Got %d byte send buffer size in %d iterations", bufsize, iter); } } /* * Set the socket receiving buffer. `bufsize` is the preferred size, * `minsize` is the smallest acceptable size. */ void k_set_rcvbuf(int socket, int bufsize, int minsize) { int delta = bufsize / 2; int iter = 0; /* * Set the socket buffer. If we can't set it as large as we * want, search around to try to find the highest acceptable * value. The highest acceptable value being smaller than * minsize is a fatal error. */ if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { bufsize -= delta; while (1) { iter++; if (delta > 1) delta /= 2; if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(bufsize)) < 0) { bufsize -= delta; } else { if (delta < 1024) break; bufsize += delta; } } if (bufsize < minsize) { logit(LOG_ERR, 0, "OS-allowed recv buffer size %u < app min %u", bufsize, minsize); /*NOTREACHED*/ } } IF_DEBUG(DEBUG_KERN) { logit(LOG_DEBUG, 0, "Got %d byte recv buffer size in %d iterations", bufsize, iter); } } /* * Set/reset the IP_HDRINCL option. My guess is we don't need it for raw * sockets, but having it here won't hurt. Well, unless you are running * an older version of FreeBSD (older than 2.2.2). If the multicast * raw packet is bigger than 208 bytes, then IP_HDRINCL triggers a bug * in the kernel and "panic". The kernel patch for netinet/ip_raw.c * coming with this distribution fixes it. */ void k_hdr_include(int socket, int val) { #ifdef IP_HDRINCL if (setsockopt(socket, IPPROTO_IP, IP_HDRINCL, (char *)&val, sizeof(val)) < 0) logit(LOG_ERR, errno, "Failed %s IP_HDRINCL on socket %d", ENABLINGSTR(val), socket); #endif } /* * Set the default TTL for the multicast packets outgoing from this * socket. * TODO: Does it affect the unicast packets? */ void k_set_ttl(int socket __attribute__((unused)), int t) { #ifdef RAW_OUTPUT_IS_RAW curttl = t; #else uint8_t ttl; ttl = t; if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) < 0) logit(LOG_ERR, errno, "Failed setting IP_MULTICAST_TTL %u on socket %d", ttl, socket); #endif } /* * Set/reset the IP_MULTICAST_LOOP. Set/reset is specified by "flag". */ void k_set_loop(int socket, int flag) { uint8_t loop; loop = flag; if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop)) < 0) logit(LOG_ERR, errno, "Failed %s IP_MULTICAST_LOOP on socket %d", ENABLINGSTR(flag), socket); } /* * Set the IP_MULTICAST_IF option on local interface ifa. */ void k_set_if(int socket, uint32_t ifa) { struct in_addr adr; adr.s_addr = ifa; if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&adr, sizeof(adr)) < 0) { if (errno == EADDRNOTAVAIL || errno == EINVAL) return; logit(LOG_ERR, errno, "Failed setting IP_MULTICAST_IF option on %s", inet_fmt(adr.s_addr, s1, sizeof(s1))); } } /* * Set Router Alert IP option, RFC2113 */ void k_set_router_alert(int socket) { char router_alert[4]; /* Router Alert IP Option */ router_alert[0] = IPOPT_RA; /* Router Alert */ router_alert[1] = 4; /* 4 bytes */ router_alert[2] = 0; router_alert[3] = 0; if (setsockopt(socket, IPPROTO_IP, IP_OPTIONS, router_alert, sizeof(router_alert) )< 0) logit(LOG_ERR, errno, "setsockopt IP_OPTIONS IPOPT_RA"); } /* * Join a multicast group on virtual interface 'v'. */ void k_join(int socket, uint32_t grp, struct uvif *v) { #ifdef __linux__ struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif /* __linux__ */ #ifdef __linux__ mreq.imr_ifindex = v->uv_ifindex; mreq.imr_address.s_addr = v->uv_lcl_addr; #else mreq.imr_interface.s_addr = v->uv_lcl_addr; #endif /* __linux__ */ mreq.imr_multiaddr.s_addr = grp; if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { #ifdef __linux__ logit(LOG_WARNING, errno, "Cannot join group %s on interface %s (ifindex %d)", inet_fmt(grp, s1, sizeof(s1)), inet_fmt(v->uv_lcl_addr, s2, sizeof(s2)), v->uv_ifindex); #else logit(LOG_WARNING, errno, "Cannot join group %s on interface %s", inet_fmt(grp, s1, sizeof(s1)), inet_fmt(v->uv_lcl_addr, s2, sizeof(s2))); #endif /* __linux__ */ } } /* * Leave a multicast group on virtual interface 'v'. */ void k_leave(int socket, uint32_t grp, struct uvif *v) { #ifdef __linux__ struct ip_mreqn mreq; #else struct ip_mreq mreq; #endif /* __linux__ */ #ifdef __linux__ mreq.imr_ifindex = v->uv_ifindex; mreq.imr_address.s_addr = v->uv_lcl_addr; #else mreq.imr_interface.s_addr = v->uv_lcl_addr; #endif /* __linux__ */ mreq.imr_multiaddr.s_addr = grp; if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { #ifdef __linux__ logit(LOG_WARNING, errno, "Cannot leave group %s on interface %s (ifindex %d)", inet_fmt(grp, s1, sizeof(s1)), inet_fmt(v->uv_lcl_addr, s2, sizeof(s2)), v->uv_ifindex); #else logit(LOG_WARNING, errno, "Cannot leave group %s on interface %s", inet_fmt(grp, s1, sizeof(s1)), inet_fmt(v->uv_lcl_addr, s2, sizeof(s2))); #endif /* __linux__ */ } } /* * Fill struct vifctl using corresponding fields from struct uvif. */ static void uvif_to_vifctl(struct vifctl *vc, struct uvif *v) { /* XXX: we don't support VIFF_TUNNEL; VIFF_SRCRT is obsolete */ vc->vifc_flags = 0; if (v->uv_flags & VIFF_REGISTER) vc->vifc_flags |= VIFF_REGISTER; vc->vifc_threshold = v->uv_threshold; vc->vifc_rate_limit = v->uv_rate_limit; vc->vifc_lcl_addr.s_addr = v->uv_lcl_addr; vc->vifc_rmt_addr.s_addr = v->uv_rmt_addr; } /* * Add a virtual interface in the kernel. */ void k_add_vif(int socket, vifi_t vifi, struct uvif *v) { struct vifctl vc; vc.vifc_vifi = vifi; uvif_to_vifctl(&vc, v); if (setsockopt(socket, IPPROTO_IP, MRT_ADD_VIF, (char *)&vc, sizeof(vc)) < 0) logit(LOG_ERR, errno, "Failed adding VIF %d (MRT_ADD_VIF) for iface %s", vifi, v->uv_name); } /* * Delete a virtual interface in the kernel. */ void k_del_vif(int socket, vifi_t vifi, struct uvif *v __attribute__((unused))) { /* * Unfortunately Linux MRT_DEL_VIF API differs a bit from the *BSD one. It * expects to receive a pointer to struct vifctl that corresponds to the VIF * we're going to delete. *BSD systems on the other hand exepect only the * index of that VIF. */ #ifdef __linux__ struct vifctl vc; vc.vifc_vifi = vifi; uvif_to_vifctl(&vc, v); /* 'v' is used only on Linux systems. */ if (setsockopt(socket, IPPROTO_IP, MRT_DEL_VIF, (char *)&vc, sizeof(vc)) < 0) #else /* *BSD et al. */ if (setsockopt(socket, IPPROTO_IP, MRT_DEL_VIF, (char *)&vifi, sizeof(vifi)) < 0) #endif /* !__linux__ */ { if (errno == EADDRNOTAVAIL || errno == EINVAL) return; logit(LOG_ERR, errno, "Failed removing VIF %d (MRT_DEL_VIF)", vifi); } } /* * Delete all MFC entries for particular routing entry from the kernel. */ int k_del_mfc(int socket, uint32_t source, uint32_t group) { struct mfcctl mc; memset(&mc, 0, sizeof(mc)); mc.mfcc_origin.s_addr = source; mc.mfcc_mcastgrp.s_addr = group; if (setsockopt(socket, IPPROTO_IP, MRT_DEL_MFC, (char *)&mc, sizeof(mc)) < 0) { logit(LOG_WARNING, errno, "Failed removing MFC entry src %s, grp %s", inet_fmt(mc.mfcc_origin.s_addr, s1, sizeof(s1)), inet_fmt(mc.mfcc_mcastgrp.s_addr, s2, sizeof(s2))); return FALSE; } logit(LOG_INFO, 0, "Removed MFC entry src %s, grp %s", inet_fmt(mc.mfcc_origin.s_addr, s1, sizeof(s1)), inet_fmt(mc.mfcc_mcastgrp.s_addr, s2, sizeof(s2))); return TRUE; } /* * Install/modify a MFC entry in the kernel */ int k_chg_mfc(int socket, uint32_t source, uint32_t group, vifi_t iif, vifbitmap_t oifs, uint32_t rp_addr __attribute__((unused))) { char input[IFNAMSIZ], output[MAXVIFS * (IFNAMSIZ + 2)] = ""; vifi_t vifi; struct uvif *v; struct mfcctl mc; memset(&mc, 0, sizeof(mc)); mc.mfcc_origin.s_addr = source; mc.mfcc_mcastgrp.s_addr = group; mc.mfcc_parent = iif; /* * draft-ietf-pim-sm-v2-new-05.txt section 4.2 mentions iif is removed * at the packet forwarding phase */ VIFM_CLR(mc.mfcc_parent, oifs); for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (VIFM_ISSET(vifi, oifs)) { mc.mfcc_ttls[vifi] = v->uv_threshold; if (output[0] != 0) strlcat(output, ", ", sizeof(output)); strlcat(output, v->uv_name, sizeof(output)); } else { mc.mfcc_ttls[vifi] = 0; } } strlcpy(input, uvifs[iif].uv_name, sizeof(input)); #ifdef PIM_REG_KERNEL_ENCAP mc.mfcc_rp_addr.s_addr = rp_addr; #endif if (setsockopt(socket, IPPROTO_IP, MRT_ADD_MFC, (char *)&mc, sizeof(mc)) < 0) { logit(LOG_WARNING, errno, "Failed adding MFC entry src %s grp %s from %s to %s", inet_fmt(mc.mfcc_origin.s_addr, s1, sizeof(s1)), inet_fmt(mc.mfcc_mcastgrp.s_addr, s2, sizeof(s2)), input, output); return FALSE; } logit(LOG_INFO, 0, "Added kernel MFC entry src %s grp %s from %s to %s", inet_fmt(mc.mfcc_origin.s_addr, s1, sizeof(s1)), inet_fmt(mc.mfcc_mcastgrp.s_addr, s2, sizeof(s2)), input, output); return TRUE; } /* * Get packet counters for particular interface * XXX: TODO: currently not used, but keep just in case we need it later. */ int k_get_vif_count(vifi_t vifi, struct vif_count *retval) { struct sioc_vif_req vreq; memset(&vreq, 0, sizeof(vreq)); vreq.vifi = vifi; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) { logit(LOG_WARNING, errno, "Failed reading kernel packet count (SIOCGETVIFCNT) on vif %d", vifi); retval->icount = retval->ocount = retval->ibytes = retval->obytes = 0xffffffff; return 1; } retval->icount = vreq.icount; retval->ocount = vreq.ocount; retval->ibytes = vreq.ibytes; retval->obytes = vreq.obytes; return 0; } /* * Gets the number of packets, bytes, and number op packets arrived * on wrong if in the kernel for particular (S,G) entry. */ int k_get_sg_cnt(int socket, uint32_t source, uint32_t group, struct sg_count *retval) { struct sioc_sg_req sgreq; memset(&sgreq, 0, sizeof(sgreq)); sgreq.src.s_addr = source; sgreq.grp.s_addr = group; if ((ioctl(socket, SIOCGETSGCNT, (char *)&sgreq) < 0) || (sgreq.wrong_if == 0xffffffff)) { /* XXX: ipmulti-3.5 has bug in ip_mroute.c, get_sg_cnt(): * the return code is always 0, so this is why we need to check * the wrong_if value. */ logit(LOG_WARNING, errno, "Failed reading kernel count (SIOCGETSGCNT) for (S,G) on (%s, %s)", inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2))); retval->pktcnt = retval->bytecnt = retval->wrong_if = ~0; return 1; } retval->pktcnt = sgreq.pktcnt; retval->bytecnt = sgreq.bytecnt; retval->wrong_if = sgreq.wrong_if; return 0; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/libite/000077500000000000000000000000001267035112600137225ustar00rootroot00000000000000pimd-2.3.2/main.c000066400000000000000000000546701267035112600135560ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: main.c,v 1.19 2003/02/12 21:56:04 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #include #include #include char versionstring[100]; int disable_all_by_default = 0; int haveterminal = 1; struct rp_hold *g_rp_hold = NULL; int mrt_table_id = 0; char *config_file = _PATH_PIMD_CONF; extern int loglevel; extern char todaysversion[]; static int sighandled = 0; #define GOT_SIGINT 0x01 #define GOT_SIGHUP 0x02 #define GOT_SIGUSR1 0x04 #define GOT_SIGUSR2 0x08 #define GOT_SIGALRM 0x10 #define NHANDLERS 3 static struct ihandler { int fd; /* File descriptor */ ihfunc_t func; /* Function to call with &fd_set */ } ihandlers[NHANDLERS]; static int nhandlers = 0; static struct debugname { char *name; uint32_t level; size_t nchars; } debugnames[] = { { "dvmrp_detail", DEBUG_DVMRP_DETAIL, 5 }, { "dvmrp_prunes", DEBUG_DVMRP_PRUNE, 8 }, { "dvmrp_pruning", DEBUG_DVMRP_PRUNE, 8 }, { "dvmrp_routes", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_routing", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_mrt", DEBUG_DVMRP_ROUTE, 7 }, { "dvmrp_neighbors", DEBUG_DVMRP_PEER, 7 }, { "dvmrp_peers", DEBUG_DVMRP_PEER, 8 }, { "dvmrp_hello", DEBUG_DVMRP_PEER, 7 }, { "dvmrp_timers", DEBUG_DVMRP_TIMER, 7 }, { "dvmrp", DEBUG_DVMRP, 1 }, { "igmp_proto", DEBUG_IGMP_PROTO, 6 }, { "igmp_timers", DEBUG_IGMP_TIMER, 6 }, { "igmp_members", DEBUG_IGMP_MEMBER, 6 }, { "groups", DEBUG_MEMBER, 1 }, { "membership", DEBUG_MEMBER, 2 }, { "igmp", DEBUG_IGMP, 1 }, { "trace", DEBUG_TRACE, 2 }, { "mtrace", DEBUG_TRACE, 2 }, { "traceroute", DEBUG_TRACE, 2 }, { "timeout", DEBUG_TIMEOUT, 2 }, { "callout", DEBUG_TIMEOUT, 3 }, { "packets", DEBUG_PKT, 2 }, { "pkt", DEBUG_PKT, 2 }, { "interfaces", DEBUG_IF, 2 }, { "vif", DEBUG_IF, 1 }, { "kernel", DEBUG_KERN, 2 }, { "cache", DEBUG_MFC, 1 }, { "mfc", DEBUG_MFC, 2 }, { "k_cache", DEBUG_MFC, 2 }, { "k_mfc", DEBUG_MFC, 2 }, { "rsrr", DEBUG_RSRR, 2 }, { "pim_detail", DEBUG_PIM_DETAIL, 5 }, { "pim_hello", DEBUG_PIM_HELLO, 5 }, { "pim_neighbors", DEBUG_PIM_HELLO, 5 }, { "pim_peers", DEBUG_PIM_HELLO, 5 }, { "pim_register", DEBUG_PIM_REGISTER, 5 }, { "registers", DEBUG_PIM_REGISTER, 2 }, { "pim_join_prune", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_j_p", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_jp", DEBUG_PIM_JOIN_PRUNE, 5 }, { "pim_bootstrap", DEBUG_PIM_BOOTSTRAP, 5 }, { "pim_bsr", DEBUG_PIM_BOOTSTRAP, 5 }, { "bsr", DEBUG_PIM_BOOTSTRAP, 1 }, { "bootstrap", DEBUG_PIM_BOOTSTRAP, 1 }, { "pim_asserts", DEBUG_PIM_ASSERT, 5 }, { "pim_cand_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_c_rp", DEBUG_PIM_CAND_RP, 5 }, { "pim_rp", DEBUG_PIM_CAND_RP, 6 }, { "rp", DEBUG_PIM_CAND_RP, 2 }, { "pim_routes", DEBUG_PIM_MRT, 6 }, { "pim_routing", DEBUG_PIM_MRT, 6 }, { "pim_mrt", DEBUG_PIM_MRT, 5 }, { "pim_timers", DEBUG_PIM_TIMER, 5 }, { "pim_rpf", DEBUG_PIM_RPF, 6 }, { "rpf", DEBUG_RPF, 3 }, { "pim", DEBUG_PIM, 1 }, { "routes", DEBUG_MRT, 1 }, { "routing", DEBUG_MRT, 1 }, { "mrt", DEBUG_MRT, 1 }, { "neighbors", DEBUG_NEIGHBORS, 1 }, { "routers", DEBUG_NEIGHBORS, 6 }, { "mrouters", DEBUG_NEIGHBORS, 7 }, { "peers", DEBUG_NEIGHBORS, 1 }, { "timers", DEBUG_TIMER, 1 }, { "asserts", DEBUG_ASSERT, 1 }, { "all", DEBUG_ALL, 2 }, { "3", 0xffffffff, 1 } /* compat. */ }; /* * Forward declarations. */ static void handler (int); static void timer (void *); static void cleanup (void); static void restart (int); static void resetlogging (void *); int register_input_handler(int fd, ihfunc_t func) { if (nhandlers >= NHANDLERS) return -1; ihandlers[nhandlers].fd = fd; ihandlers[nhandlers++].func = func; return 0; } static void do_randomize(void) { #define rol32(data,shift) ((data) >> (shift)) | ((data) << (32 - (shift))) int fd; unsigned int seed; /* Setup a fallback seed based on quasi random. */ #ifdef SYSV seed = time(NULL); #else seed = time(NULL) ^ gethostid(); #endif seed = rol32(seed, seed); fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { if (-1 == read(fd, &seed, sizeof(seed))) warn("Failed reading entropy from /dev/urandom"); close(fd); } #ifdef SYSV srand48(seed); #else srandom(seed); #endif } /* Figure out the PID of a running daemon. */ static pid_t daemon_pid(void) { int result; char *path = NULL; FILE *fp; pid_t pid = -1; result = asprintf(&path, "%s%s.pid", _PATH_VARRUN, __progname); if (result == -1 || path == NULL) return -1; fp = fopen(path, "r"); if (!fp) { free(path); return -1; } result = fscanf(fp, "%d", &pid); fclose(fp); free(path); return pid; } /* Send signal to running daemon and the show resulting file. */ static int killshow(int signo, char *file) { pid_t pid = daemon_pid(); char buf[100]; if (pid > 0) { if (file && -1 == remove(file) && errno != ENOENT) warn("Failed removing %s, may be showing stale information", file); kill(pid, signo); if (file) { usleep(200); snprintf(buf, sizeof(buf), "cat %s", file); if (-1 == system(buf)) { warnx("Failed listing file %s\n", file); } } } return 0; } static int usage(int code) { size_t i; char line[76] = " "; struct debugname *d; printf("\nUsage: %s [-fhlNqrv] [-c FILE] [-d [SYS][,SYS...]] [-s LEVEL]\n\n", __progname); printf(" -c, --config=FILE Configuration file to use, default %s\n", _PATH_PIMD_CONF); printf(" -d, --debug[=SYS] Debug subsystem, see below for valid systems, default all\n"); printf(" -f, --foreground Run in foreground, do not detach from calling terminal\n"); printf(" -h, --help Show this help text\n"); /* printf(" -i, --show-cache Show internal cache tables\n"); */ printf(" -l, --reload-config Tell a running pimd to reload its configuration\n"); printf(" -N, --disable-vifs Disable all virtual interfaces (phyint) by default\n"); /* printf(" -p,--show-debug Show debug dump, only if debug is enabled\n"); */ printf(" -q, --quit-daemon Send SIGTERM to a running pimd\n"); printf(" -r, --show-routes Show state of VIFs and multicast routing tables\n"); printf(" -t, --table-id=ID Set multicast routing table ID. Allowed table ID#:\n" " 0 .. 999999999. Default: 0 (use default table)\n"); printf(" -s, --loglevel=LVL Set log level: none, err, info, notice (default), debug\n"); printf(" -v, --version Show %s version\n", __progname); printf("\n"); /* From pimd v2.3.0 we show *all* the debug levels again */ printf("Available subsystems for debug:\n"); for (i = 0, d = debugnames; i < ARRAY_LEN(debugnames); i++, d++) { if (strlen(line) + strlen(d->name) + 3 >= sizeof(line)) { /* Finish this line and send to console */ strlcat(line, "\n", sizeof(line)); printf("%s", line); /* Prepare for next line */ strlcpy(line, " ", sizeof(line)); } strlcat(line, d->name, sizeof(line)); if (i + 1 < ARRAY_LEN(debugnames)) strlcat(line, ", ", sizeof(line)); } /* Flush remaining line. */ strlcat(line, "\n", sizeof(line)); printf("%s", line); printf("\nBug report address: %-40s\n\n", PACKAGE_BUGREPORT); return code; } int main(int argc, char *argv[]) { int dummysigalrm, foreground = 0; struct timeval tv, difftime, curtime, lasttime, *timeout; fd_set rfds, readers; int nfds, n, i, secs, ch; struct sigaction sa; time_t boottime; struct option long_options[] = { {"config", 1, 0, 'c'}, {"debug", 2, 0, 'd'}, {"foreground", 0, 0, 'f'}, {"disable-vifs", 0, 0, 'N'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"quit-daemon", 0, 0, 'q'}, {"reload-config", 0, 0, 'l'}, {"show-routes", 0, 0, 'r'}, {"table-id", 1, 0, 't'}, {"syslog-level", 1, 0, 's'}, /* Compat */ {"loglevel", 1, 0, 's'}, /* {"show-cache", 0, 0, 'i'}, */ /* {"show-debug", 0, 0, 'p'}, */ {0, 0, 0, 0} }; snprintf(versionstring, sizeof (versionstring), "pimd version %s", todaysversion); while ((ch = getopt_long(argc, argv, "c:d::fhlNvqrt:s:", long_options, NULL)) != EOF) { const char *errstr; switch (ch) { case 'c': if (optarg) config_file = optarg; break; case 'd': if (!optarg) { debug = DEBUG_DEFAULT; } else { char *p,*q; size_t i, len; struct debugname *d; debug = 0; p = optarg; q = NULL; while (p) { q = strchr(p, ','); if (q) *q++ = '\0'; len = strlen(p); for (i = 0, d = debugnames; i < ARRAY_LEN(debugnames); i++, d++) { if (len >= d->nchars && strncmp(d->name, p, len) == 0) break; } if (i == ARRAY_LEN(debugnames)) return usage(1); debug |= d->level; p = q; } } break; case 'f': foreground = 1; break; case 'h': return usage(0); case 'l': return killshow(SIGHUP, NULL); case 'N': disable_all_by_default = 1; break; case 'v': printf("%s\n", versionstring); return 0; case 'q': return killshow(SIGTERM, NULL); case 'r': return killshow(SIGUSR1, _PATH_PIMD_DUMP); case 's': if (!optarg) { fprintf(stderr, "Missing loglevel argument!\n"); return usage(1); } loglevel = loglvl(optarg); if (-1 == loglevel) return usage(1); break; case 't': if (!optarg) { fprintf(stderr, "Missing Table ID argument!\n"); return usage(1); } mrt_table_id = strtonum(optarg, 0, 999999999, &errstr); if (errstr) { fprintf(stderr, "Table ID %s!\n", errstr); return usage(1); } break; #if 0 /* XXX: TODO */ case 'i': return killshow(SIGUSR2, _PATH_PIMD_CACHE); case 'p': return killshow(SIGQUIT, NULL); #endif default: return usage(1); } } argc -= optind; if (argc > 0) return usage(1); if (geteuid() != 0) errx(1, "Need root privileges to start."); setlinebuf(stderr); if (debug != 0) { struct debugname *d; char c; int tmpd = debug; fprintf(stderr, "debug level 0x%lx ", debug); c = '('; for (d = debugnames; d < debugnames + ARRAY_LEN(debugnames); d++) { if ((tmpd & d->level) == d->level) { tmpd &= ~d->level; fprintf(stderr, "%c%s", c, d->name); c = ','; } } fprintf(stderr, ")\n"); } /* * Create directory for runtime files */ if (-1 == mkdir(_PATH_PIMD_RUNDIR, 0755) && errno != EEXIST) err(1, "Failed creating %s directory for runtime files", _PATH_PIMD_RUNDIR); /* * Setup logging */ #ifdef LOG_DAEMON openlog("pimd", LOG_PID, LOG_DAEMON); setlogmask(LOG_UPTO(loglevel)); #else openlog("pimd", LOG_PID); #endif /* LOG_DAEMON */ logit(LOG_NOTICE, 0, "%s starting ...", versionstring); do_randomize(); time(&boottime); callout_init(); init_igmp(); init_pim(); init_routesock(); /* Both for Linux netlink and BSD routing socket */ init_pim_mrt(); init_timers(); /* Start up the log rate-limiter */ resetlogging(NULL); /* TODO: check the kernel DVMRP/MROUTED/PIM support version */ init_vifs(); init_rp_and_bsr(); /* Must be after init_vifs() */ #ifdef RSRR rsrr_init(); #endif /* RSRR */ sa.sa_handler = handler; sa.sa_flags = 0; /* Interrupt system calls */ sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } IF_DEBUG(DEBUG_IF) dump_vifs(stderr); IF_DEBUG(DEBUG_PIM_MRT) dump_pim_mrt(stderr); /* schedule first timer interrupt */ timer_setTimer(TIMER_INTERVAL, timer, NULL); if (!debug && !foreground) { /* Detach from the terminal */ haveterminal = 0; if (fork()) exit(0); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); n = open("/dev/null", O_RDWR, 0); if (n >= 0) { dup2(n, STDIN_FILENO); dup2(n, STDOUT_FILENO); dup2(n, STDERR_FILENO); } #ifdef SYSV setpgrp(); #else #ifdef TIOCNOTTY n = open("/dev/tty", 2); if (n >= 0) { (void)ioctl(n, TIOCNOTTY, (char *)0); (void)close(n); } #else if (setsid() < 0) perror("setsid"); #endif /* TIOCNOTTY */ #endif /* SYSV */ } /* End of child process code */ if (pidfile(NULL)) warn("Cannot create pidfile"); /* * Main receive loop. */ dummysigalrm = SIGALRM; difftime.tv_usec = 0; gettimeofday(&curtime, NULL); lasttime = curtime; while (1) { memcpy(&rfds, &readers, sizeof(rfds)); secs = timer_nextTimer(); if (secs == -1) timeout = NULL; else { timeout = &tv; timeout->tv_sec = secs; timeout->tv_usec = 0; } if (boottime) { time_t n; time(&n); if (n > boottime + 15) { struct rp_hold *rph = g_rp_hold; while(rph) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, rph->address, 1, (uint16_t)0xffffff, rph->group, rph->mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); rph = rph->next; } boottime = 0; } } if (sighandled) { if (sighandled & GOT_SIGINT) { sighandled &= ~GOT_SIGINT; break; } if (sighandled & GOT_SIGHUP) { sighandled &= ~GOT_SIGHUP; restart(SIGHUP); /* reconstruct readers and nfds */ FD_ZERO(&readers); FD_SET(igmp_socket, &readers); nfds = igmp_socket + 1; for (i = 0; i < nhandlers; i++) { FD_SET(ihandlers[i].fd, &readers); if (ihandlers[i].fd >= nfds) nfds = ihandlers[i].fd + 1; } memcpy(&rfds, &readers, sizeof(rfds)); } if (sighandled & GOT_SIGUSR1) { sighandled &= ~GOT_SIGUSR1; fdump(SIGUSR1); } if (sighandled & GOT_SIGUSR2) { sighandled &= ~GOT_SIGUSR2; cdump(SIGUSR2); } if (sighandled & GOT_SIGALRM) { sighandled &= ~GOT_SIGALRM; timer(&dummysigalrm); } } if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) { if (errno != EINTR) /* SIGALRM is expected */ logit(LOG_WARNING, errno, "select failed"); continue; } if (n > 0) { /* TODO: shall check first igmp_socket for better performance? */ for (i = 0; i < nhandlers; i++) { if (FD_ISSET(ihandlers[i].fd, &rfds)) { (*ihandlers[i].func)(ihandlers[i].fd, &rfds); } } } /* * Handle timeout queue. * * If select + packet processing took more than 1 second, * or if there is a timeout pending, age the timeout queue. * * If not, collect usec in difftime to make sure that the * time doesn't drift too badly. * * If the timeout handlers took more than 1 second, * age the timeout queue again. XXX This introduces the * potential for infinite loops! */ do { /* * If the select timed out, then there's no other * activity to account for and we don't need to * call gettimeofday. */ if (n == 0) { curtime.tv_sec = lasttime.tv_sec + secs; curtime.tv_usec = lasttime.tv_usec; n = -1; /* don't do this next time through the loop */ } else gettimeofday(&curtime, NULL); difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec; difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec; while (difftime.tv_usec >= 1000000) { difftime.tv_sec++; difftime.tv_usec -= 1000000; } if (difftime.tv_usec < 0) { difftime.tv_sec--; difftime.tv_usec += 1000000; } lasttime = curtime; if (secs == 0 || difftime.tv_sec > 0) age_callout_queue(difftime.tv_sec); secs = -1; } while (difftime.tv_sec > 0); } /* Main loop */ logit(LOG_NOTICE, 0, "%s exiting.", versionstring); cleanup(); exit(0); } /* * The 'virtual_time' variable is initialized to a value that will cause the * first invocation of timer() to send a probe or route report to all vifs * and send group membership queries to all subnets for which this router is * querier. This first invocation occurs approximately TIMER_INTERVAL seconds * after the router starts up. Note that probes for neighbors and queries * for group memberships are also sent at start-up time, as part of initial- * ization. This repetition after a short interval is desirable for quickly * building up topology and membership information in the presence of possible * packet loss. * * 'virtual_time' advances at a rate that is only a crude approximation of * real time, because it does not take into account any time spent processing, * and because the timer intervals are sometimes shrunk by a random amount to * avoid unwanted synchronization with other routers. */ uint32_t virtual_time = 0; /* * Timer routine. Performs all perodic functions: * aging interfaces, quering neighbors and members, etc... The granularity * is equal to TIMER_INTERVAL. */ static void timer(void *i __attribute__((unused))) { age_vifs(); /* Timeout neighbors and groups */ age_routes(); /* Timeout routing entries */ age_misc(); /* Timeout the rest (Cand-RP list, etc) */ virtual_time += TIMER_INTERVAL; timer_setTimer(TIMER_INTERVAL, timer, NULL); } /* * Performs all necessary functions to quit gracefully */ /* TODO: implement all necessary stuff */ static void cleanup(void) { vifi_t vifi; struct uvif *v; /* inform all neighbors that I'm going to die */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if ((v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER | VIFF_TUNNEL)) == 0) send_pim_hello(v, 0); } #ifdef RSRR rsrr_clean(); #endif /* RSRR */ /* TODO: XXX (not in the spec): if I am the BSR, somehow inform the * other routers I am going down and need to elect another BSR? * (probably by sending a the Cand-RP-set with my_priority=LOWEST?) */ k_stop_pim(igmp_socket); } /* * Signal handler. Take note of the fact that the signal arrived * so that the main loop can take care of it. */ static void handler(int sig) { switch (sig) { case SIGALRM: sighandled |= GOT_SIGALRM; break; case SIGINT: case SIGTERM: sighandled |= GOT_SIGINT; break; case SIGHUP: sighandled |= GOT_SIGHUP; break; case SIGUSR1: sighandled |= GOT_SIGUSR1; break; case SIGUSR2: sighandled |= GOT_SIGUSR2; break; } } /* TODO: not verified */ /* * Restart the daemon */ static void restart(int i __attribute__((unused))) { logit(LOG_NOTICE, 0, "%s restarting ...", versionstring); /* * reset all the entries */ /* TODO: delete? free_all_routes(); */ free_all_callouts(); stop_all_vifs(); k_stop_pim(igmp_socket); nhandlers = 0; close(igmp_socket); close(pim_socket); /* * When IOCTL_OK_ON_RAW_SOCKET is defined, 'udp_socket' is equal * 'to igmp_socket'. Therefore, 'udp_socket' should be closed only * if they are different. */ #ifndef IOCTL_OK_ON_RAW_SOCKET close(udp_socket); #endif /* Both for Linux netlink and BSD routing socket */ close(routing_socket); /* * start processing again */ init_igmp(); init_pim(); init_routesock(); /* Both for Linux netlink and BSD routing socket */ init_pim_mrt(); init_vifs(); /* schedule timer interrupts */ timer_setTimer(TIMER_INTERVAL, timer, NULL); } static void resetlogging(void *arg) { int nxttime = 60; void *narg = NULL; if (arg == NULL && log_nmsgs >= LOG_MAX_MSGS) { nxttime = LOG_SHUT_UP; narg = (void *)&log_nmsgs; /* just need some valid void * */ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes", LOG_SHUT_UP / 60); } else { if (arg != NULL) { syslog(LOG_NOTICE, "logging enabled again after rate limiting"); } log_nmsgs = 0; } timer_setTimer(nxttime, resetlogging, narg); } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/mrt.c000066400000000000000000001002251267035112600134200ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: mrt.c,v 1.27 2001/09/10 20:31:36 pavlin Exp $ */ #include "defs.h" srcentry_t *srclist; grpentry_t *grplist; /* * Local functions definition */ static srcentry_t *create_srcentry (uint32_t); static int search_srclist (uint32_t, srcentry_t **); static int search_srcmrtlink (srcentry_t *, uint32_t, mrtentry_t **); static void insert_srcmrtlink (mrtentry_t *, mrtentry_t *, srcentry_t *); static grpentry_t *create_grpentry (uint32_t); static int search_grplist (uint32_t, grpentry_t **); static int search_grpmrtlink (grpentry_t *, uint32_t, mrtentry_t **); static void insert_grpmrtlink (mrtentry_t *, mrtentry_t *, grpentry_t *); static mrtentry_t *alloc_mrtentry (srcentry_t *, grpentry_t *); static mrtentry_t *create_mrtentry (srcentry_t *, grpentry_t *, uint16_t); static void move_kernel_cache (mrtentry_t *, uint16_t); void init_pim_mrt(void) { /* TODO: delete any existing routing table */ /* Initialize the source list */ /* The first entry has address 'INADDR_ANY' and is not used */ /* The order is the smallest address first. */ srclist = (srcentry_t *)calloc(1, sizeof(srcentry_t)); if (!srclist) logit(LOG_ERR, 0, "Ran out of memory in init_pim_mrt()"); srclist->next = NULL; srclist->prev = NULL; srclist->address = INADDR_ANY_N; srclist->mrtlink = NULL; srclist->incoming = NO_VIF; srclist->upstream = NULL; srclist->metric = 0; srclist->preference = 0; RESET_TIMER(srclist->timer); srclist->cand_rp = NULL; /* Initialize the group list */ /* The first entry has address 'INADDR_ANY' and is not used */ /* The order is the smallest address first. */ grplist = (grpentry_t *)calloc(1, sizeof(grpentry_t)); if (!grplist) logit(LOG_ERR, 0, "Ran out of memory in init_pim_mrt()"); grplist->next = NULL; grplist->prev = NULL; grplist->rpnext = NULL; grplist->rpprev = NULL; grplist->group = INADDR_ANY_N; grplist->rpaddr = INADDR_ANY_N; grplist->mrtlink = NULL; grplist->active_rp_grp = NULL; grplist->grp_route = NULL; } grpentry_t *find_group(uint32_t group) { grpentry_t *grp; if (!IN_MULTICAST(ntohl(group))) return NULL; if (search_grplist(group, &grp) == TRUE) return grp; /* Group found! */ return NULL; } srcentry_t *find_source(uint32_t source) { srcentry_t *src; if (!inet_valid_host(source)) return NULL; if (search_srclist(source, &src) == TRUE) return src; /* Source found! */ return NULL; } mrtentry_t *find_route(uint32_t source, uint32_t group, uint16_t flags, char create) { srcentry_t *src = NULL; grpentry_t *grp = NULL; mrtentry_t *mrt = NULL; mrtentry_t *mrt_wc = NULL; mrtentry_t *mrt_pmbr = NULL; mrtentry_t *mrt2 = NULL; rpentry_t *rp = NULL; rp_grp_entry_t *rp_grp = NULL; if (flags & (MRTF_SG | MRTF_WC)) { if (!IN_MULTICAST(ntohl(group))) { logit(LOG_WARNING, 0, "find_route: Not a multicast group address (%s) ...", inet_fmt(group, s1, sizeof(s1))); return NULL; } } if (flags & MRTF_SG) { if (!inet_valid_host(source) && !IN_PIM_SSM_RANGE(group)) { logit(LOG_WARNING, 0, "find_route: Not a valid host (%s) ...", inet_fmt(source, s1, sizeof(s1))); return NULL; } } if (create == DONT_CREATE) { if (flags & (MRTF_SG | MRTF_WC)) { if (search_grplist(group, &grp) == FALSE) { /* Group not found. Return the (*,*,RP) entry */ if (flags & MRTF_PMBR) { rp = rp_match(group); if (rp) { logit(LOG_DEBUG, 0 , "find_route: Group not found. Return the (*,*,RP) entry"); return rp->mrtlink; } } logit(LOG_DEBUG, 0 , "find_route: Not PMBR, return NULL"); return NULL; } /* Search for the source */ if (flags & MRTF_SG) { if (search_grpmrtlink(grp, source, &mrt) == TRUE) { /* Exact (S,G) entry found */ logit(LOG_DEBUG, 0 , "find_route: exact (S,G) entry found"); return mrt; } logit(LOG_DEBUG, 0 , "find_route:(S,G) entry not found"); } /* No (S,G) entry. Return the (*,G) entry (if exist) */ if ((flags & MRTF_WC) && grp->grp_route) { logit(LOG_DEBUG, 0 , "find_route: No (S,G) entry. Return the (*,G) entry"); return grp->grp_route; } } /* Return the (*,*,RP) entry */ if (flags & MRTF_PMBR) { rp = NULL; if (group != INADDR_ANY_N) rp = rp_match(group); else if (source != INADDR_ANY_N) rp = rp_find(source); if (rp) { logit(LOG_DEBUG, 0 , "find_route: Return the (*,*,RP) entry"); return rp->mrtlink; } } logit(LOG_DEBUG, 0 , "find_route: No SG|WC, return NULL"); return NULL; } /* Creation allowed */ if (flags & (MRTF_SG | MRTF_WC)) { grp = create_grpentry(group); if (!grp) return NULL; if (IN_PIM_SSM_RANGE(group)) { if (rp_match(group) == (rpentry_t *) NULL) { /* For SSM, virtual RP entry has to be created. RP is at local link 169.254.0.1 to be sure not to send any register messages outside, although sending them has been disabled for SSM also in PIM protocol. The address does not need to be really configured in any interface. TODO: Avoid need for virtual RP by implementing SSM-specific state structures */ add_rp_grp_entry(&cand_rp_list, &grp_mask_list, 0x0100fea9, 20, 90, group, 0xffffffff, curr_bsr_hash_mask, curr_bsr_fragment_tag); } } if (!grp->active_rp_grp) { rp_grp = rp_grp_match(group); if (!rp_grp) { if (!grp->mrtlink && !grp->grp_route) /* New created grpentry. Delete it. */ delete_grpentry(grp); return NULL; } rp = rp_grp->rp->rpentry; grp->active_rp_grp = rp_grp; grp->rpaddr = rp->address; /* Link to the top of the rp_grp_chain */ grp->rpnext = rp_grp->grplink; rp_grp->grplink = grp; if (grp->rpnext) grp->rpnext->rpprev = grp; } else { rp = grp->active_rp_grp->rp->rpentry; } } mrt_wc = mrt_pmbr = NULL; if (flags & MRTF_WC) { /* Setup the (*,G) routing entry */ mrt_wc = create_mrtentry(NULL, grp, MRTF_WC); if (!mrt_wc) { if (!grp->mrtlink) /* New created grpentry. Delete it. */ delete_grpentry(grp); return NULL; } if (mrt_wc->flags & MRTF_NEW) { mrt_pmbr = rp->mrtlink; /* Copy the oif list from the (*,*,RP) entry */ if (mrt_pmbr) VOIF_COPY(mrt_pmbr, mrt_wc); mrt_wc->incoming = rp->incoming; mrt_wc->upstream = rp->upstream; mrt_wc->metric = rp->metric; mrt_wc->preference = rp->preference; move_kernel_cache(mrt_wc, 0); #ifdef RSRR rsrr_cache_bring_up(mrt_wc); #endif /* RSRR */ } if (!(flags & MRTF_SG)) return mrt_wc; } if (flags & MRTF_SG) { /* Setup the (S,G) routing entry */ src = create_srcentry(source); if (!src) { /* TODO: XXX: The MRTF_NEW flag check may be misleading?? check */ if ((!grp->grp_route || (grp->grp_route && (grp->grp_route->flags & MRTF_NEW))) && !grp->mrtlink) { /* New created grpentry. Delete it. */ delete_grpentry(grp); } return NULL; } mrt = create_mrtentry(src, grp, MRTF_SG); if (!mrt) { if ((!grp->grp_route || (grp->grp_route && (grp->grp_route->flags & MRTF_NEW))) && !grp->mrtlink) { /* New created grpentry. Delete it. */ delete_grpentry(grp); } if (!src->mrtlink) /* New created srcentry. Delete it. */ delete_srcentry(src); return NULL; } if (mrt->flags & MRTF_NEW) { mrt2 = grp->grp_route; if (!mrt2) mrt2 = rp->mrtlink; /* Copy the oif list from the existing (*,G) or (*,*,RP) entry */ if (mrt2) { VOIF_COPY(mrt2, mrt); if (flags & MRTF_RP) { /* ~(S,G) prune entry */ mrt->incoming = mrt2->incoming; mrt->upstream = mrt2->upstream; mrt->metric = mrt2->metric; mrt->preference = mrt2->preference; mrt->flags |= MRTF_RP; } } if (!(mrt->flags & MRTF_RP)) { mrt->incoming = src->incoming; mrt->upstream = src->upstream; mrt->metric = src->metric; mrt->preference = src->preference; } move_kernel_cache(mrt, 0); #ifdef RSRR rsrr_cache_bring_up(mrt); #endif /* RSRR */ } return mrt; } if (flags & MRTF_PMBR) { /* Get/return the (*,*,RP) routing entry */ if (group != INADDR_ANY_N) rp = rp_match(group); else if (source != INADDR_ANY_N) rp = rp_find(source); else return NULL; /* source == group == INADDR_ANY */ if (!rp) return NULL; if (rp->mrtlink) return rp->mrtlink; mrt = create_mrtentry(rp, NULL, MRTF_PMBR); if (!mrt) return NULL; mrt->incoming = rp->incoming; mrt->upstream = rp->upstream; mrt->metric = rp->metric; mrt->preference = rp->preference; return mrt; } return NULL; } void delete_srcentry(srcentry_t *src) { mrtentry_t *node; mrtentry_t *next; if (!src) return; /* TODO: XXX: the first entry is unused and always there */ src->prev->next = src->next; if (src->next) src->next->prev = src->prev; for (node = src->mrtlink; node; node = next) { next = node->srcnext; if (node->flags & MRTF_KERNEL_CACHE) /* Delete the kernel cache first */ delete_mrtentry_all_kernel_cache(node); if (node->grpprev) { node->grpprev->grpnext = node->grpnext; } else { node->group->mrtlink = node->grpnext; if (!node->grpnext && !node->group->grp_route) /* Delete the group entry if it has no (*,G) routing entry */ delete_grpentry(node->group); } if (node->grpnext) node->grpnext->grpprev = node->grpprev; FREE_MRTENTRY(node); } free(src); } void delete_grpentry(grpentry_t *grp) { mrtentry_t *node; mrtentry_t *next; if (!grp) return; /* TODO: XXX: the first entry is unused and always there */ grp->prev->next = grp->next; if (grp->next) grp->next->prev = grp->prev; if (grp->grp_route) { if (grp->grp_route->flags & MRTF_KERNEL_CACHE) delete_mrtentry_all_kernel_cache(grp->grp_route); FREE_MRTENTRY(grp->grp_route); } /* Delete from the rp_grp_entry chain */ if (grp->active_rp_grp) { if (grp->rpnext) grp->rpnext->rpprev = grp->rpprev; if (grp->rpprev) grp->rpprev->rpnext = grp->rpnext; else grp->active_rp_grp->grplink = grp->rpnext; } for (node = grp->mrtlink; node; node = next) { next = node->grpnext; if (node->flags & MRTF_KERNEL_CACHE) /* Delete the kernel cache first */ delete_mrtentry_all_kernel_cache(node); if (node->srcprev) { node->srcprev->srcnext = node->srcnext; } else { node->source->mrtlink = node->srcnext; if (!node->srcnext) { /* Delete the srcentry if this was the last routing entry */ delete_srcentry(node->source); } } if (node->srcnext) node->srcnext->srcprev = node->srcprev; FREE_MRTENTRY(node); } free(grp); } void delete_mrtentry(mrtentry_t *mrt) { grpentry_t *grp; mrtentry_t *mrt_wc; mrtentry_t *mrt_rp; if (!mrt) return; /* Delete the kernel cache first */ if (mrt->flags & MRTF_KERNEL_CACHE) delete_mrtentry_all_kernel_cache(mrt); #ifdef RSRR /* Tell the reservation daemon */ rsrr_cache_clean(mrt); #endif /* RSRR */ if (mrt->flags & MRTF_PMBR) { /* (*,*,RP) mrtentry */ mrt->source->mrtlink = NULL; } else if (mrt->flags & MRTF_SG) { /* (S,G) mrtentry */ /* Delete from the grpentry MRT chain */ if (mrt->grpprev) { mrt->grpprev->grpnext = mrt->grpnext; } else { mrt->group->mrtlink = mrt->grpnext; if (!mrt->grpnext) { /* All (S,G) MRT entries are gone. Allow creating (*,G) MFC entries. */ mrt_rp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; mrt_wc = mrt->group->grp_route; if (mrt_rp) mrt_rp->flags &= ~MRTF_MFC_CLONE_SG; if (mrt_wc) mrt_wc->flags &= ~MRTF_MFC_CLONE_SG; else /* Delete the group entry if it has no (*,G) routing entry */ delete_grpentry(mrt->group); } } if (mrt->grpnext) mrt->grpnext->grpprev = mrt->grpprev; /* Delete from the srcentry MRT chain */ if (mrt->srcprev) { mrt->srcprev->srcnext = mrt->srcnext; } else { mrt->source->mrtlink = mrt->srcnext; if (!mrt->srcnext) /* Delete the srcentry if this was the last routing entry */ delete_srcentry(mrt->source); } if (mrt->srcnext) mrt->srcnext->srcprev = mrt->srcprev; } else { /* This mrtentry should be (*,G) */ grp = mrt->group; grp->grp_route = NULL; /* Delete the group entry if it has no (S,G) entries */ if (!grp->mrtlink) delete_grpentry(grp); } FREE_MRTENTRY(mrt); } static int search_srclist(uint32_t source, srcentry_t **found) { srcentry_t *prev, *node; uint32_t source_h = ntohl(source); for (prev = srclist, node = prev->next; node; prev = node, node = node->next) { /* The srclist is ordered with the smallest addresses first. * The first entry is not used. */ if (ntohl(node->address) < source_h) continue; if (node->address == source) { *found = node; return TRUE; } break; } *found = prev; /* The insertion point is between s_prev and s */ return FALSE; } static int search_grplist(uint32_t group, grpentry_t **found) { grpentry_t *prev, *node; uint32_t group_h = ntohl(group); for (prev = grplist, node = prev->next; node; prev = node, node = node->next) { /* The grplist is ordered with the smallest address first. * The first entry is not used. */ if (ntohl(node->group) < group_h) continue; if (node->group == group) { *found = node; return TRUE; } break; } *found = prev; /* The insertion point is between prev and g */ return FALSE; } static srcentry_t *create_srcentry(uint32_t source) { srcentry_t *node; srcentry_t *prev; if (search_srclist(source, &prev) == TRUE) return prev; node = calloc(1, sizeof(srcentry_t)); if (!node) { logit(LOG_WARNING, 0, "Memory allocation error for srcentry %s", inet_fmt(source, s1, sizeof(s1))); return NULL; } node->address = source; /* Free the memory if there is error getting the iif and * the next hop (upstream) router. */ if (set_incoming(node, PIM_IIF_SOURCE) == FALSE) { free(node); return NULL; } RESET_TIMER(node->timer); node->mrtlink = NULL; node->cand_rp = NULL; node->next = prev->next; prev->next = node; node->prev = prev; if (node->next) node->next->prev = node; IF_DEBUG(DEBUG_MFC) { logit(LOG_DEBUG, 0, "create source entry, source %s", inet_fmt(source, s1, sizeof(s1))); } return node; } static grpentry_t *create_grpentry(uint32_t group) { grpentry_t *node; grpentry_t *prev; /* If already exists, return it. Otheriwse search_grplist() returns the * insertion point in prev. */ if (search_grplist(group, &prev) == TRUE) return prev; node = calloc(1, sizeof(grpentry_t)); if (!node) { logit(LOG_WARNING, 0, "Memory allocation error for grpentry %s", inet_fmt(group, s1, sizeof(s1))); return NULL; } /* * TODO: XXX: Note that this is NOT a (*,G) routing entry, but simply * a group entry, probably used to search the routing table (to find * (S,G) entries for example.) * To become (*,G) routing entry, we must setup node->grp_route */ node->group = group; node->rpaddr = INADDR_ANY_N; node->mrtlink = NULL; node->active_rp_grp = NULL; node->grp_route = NULL; node->rpnext = NULL; node->rpprev = NULL; /* Now it is safe to include the new group entry */ node->next = prev->next; prev->next = node; node->prev = prev; if (node->next) node->next->prev = node; IF_DEBUG(DEBUG_MFC) { logit(LOG_DEBUG, 0, "create group entry, group %s", inet_fmt(group, s1, sizeof(s1))); } return node; } /* * Return TRUE if the entry is found and then *found is set to point to * that entry. Otherwise return FALSE and *found points the previous * entry, or NULL if first in the chain. */ static int search_srcmrtlink(srcentry_t *src, uint32_t group, mrtentry_t **found) { mrtentry_t *node; mrtentry_t *prev = NULL; uint32_t group_h = ntohl(group); for (node = src->mrtlink; node; prev = node, node = node->srcnext) { /* The entries are ordered with the smaller group address first. * The addresses are in network order. */ if (ntohl(node->group->group) < group_h) continue; if (node->group->group == group) { *found = node; return TRUE; } break; } *found = prev; return FALSE; } /* * Return TRUE if the entry is found and then *found is set to point to * that entry. Otherwise return FALSE and *found points the previous * entry, or NULL if first in the chain. */ static int search_grpmrtlink(grpentry_t *grp, uint32_t source, mrtentry_t **found) { mrtentry_t *node; mrtentry_t *prev = NULL; uint32_t source_h = ntohl(source); for (node = grp->mrtlink; node; prev = node, node = node->grpnext) { /* The entries are ordered with the smaller source address first. * The addresses are in network order. */ if (ntohl(node->source->address) < source_h) continue; if (source == node->source->address) { *found = node; return TRUE; } break; } *found = prev; return FALSE; } static void insert_srcmrtlink(mrtentry_t *node, mrtentry_t *prev, srcentry_t *src) { if (!prev) { /* Has to be insert as the head entry for this source */ node->srcnext = src->mrtlink; node->srcprev = NULL; src->mrtlink = node; } else { /* Insert right after the prev */ node->srcnext = prev->srcnext; node->srcprev = prev; prev->srcnext = node; } if (node->srcnext) node->srcnext->srcprev = node; } static void insert_grpmrtlink(mrtentry_t *node, mrtentry_t *prev, grpentry_t *grp) { if (!prev) { /* Has to be insert as the head entry for this group */ node->grpnext = grp->mrtlink; node->grpprev = NULL; grp->mrtlink = node; } else { /* Insert right after the prev */ node->grpnext = prev->grpnext; node->grpprev = prev; prev->grpnext = node; } if (node->grpnext) node->grpnext->grpprev = node; } static mrtentry_t *alloc_mrtentry(srcentry_t *src, grpentry_t *grp) { mrtentry_t *mrt; uint16_t i, *timer; uint8_t vif_numbers; mrt = calloc(1, sizeof(mrtentry_t)); if (!mrt) { logit(LOG_WARNING, 0, "alloc_mrtentry(): out of memory"); return NULL; } /* * grpnext, grpprev, srcnext, srcprev will be setup when we link the * mrtentry to the source and group chains */ mrt->source = src; mrt->group = grp; mrt->incoming = NO_VIF; VIFM_CLRALL(mrt->joined_oifs); VIFM_CLRALL(mrt->leaves); VIFM_CLRALL(mrt->pruned_oifs); VIFM_CLRALL(mrt->asserted_oifs); VIFM_CLRALL(mrt->oifs); mrt->upstream = NULL; mrt->metric = 0; mrt->preference = 0; mrt->pmbr_addr = INADDR_ANY_N; #ifdef RSRR mrt->rsrr_cache = NULL; #endif /* RSRR */ /* XXX: TODO: if we are short in memory, we can reserve as few as possible * space for vif timers (per group and/or routing entry), but then everytime * when a new interfaces is configured, the router will be restarted and * will delete the whole routing table. The "memory is cheap" solution is * to reserve timer space for all potential vifs in advance and then no * need to delete the routing table and disturb the forwarding. */ #ifdef SAVE_MEMORY mrt->vif_timers = (uint16_t *)calloc(1, sizeof(uint16_t) * numvifs); mrt->vif_deletion_delay = (uint16_t *)calloc(1, sizeof(uint16_t) * numvifs); vif_numbers = numvifs; #else mrt->vif_timers = (uint16_t *)calloc(1, sizeof(uint16_t) * total_interfaces); mrt->vif_deletion_delay = (uint16_t *)calloc(1, sizeof(uint16_t) * total_interfaces); vif_numbers = total_interfaces; #endif /* SAVE_MEMORY */ if (!mrt->vif_timers || !mrt->vif_deletion_delay) { logit(LOG_WARNING, 0, "alloc_mrtentry(): out of memory"); FREE_MRTENTRY(mrt); return NULL; } /* Reset the timers */ for (i = 0, timer = mrt->vif_timers; i < vif_numbers; i++, timer++) RESET_TIMER(*timer); for (i = 0, timer = mrt->vif_deletion_delay; i < vif_numbers; i++, timer++) RESET_TIMER(*timer); mrt->flags = MRTF_NEW; RESET_TIMER(mrt->timer); RESET_TIMER(mrt->jp_timer); RESET_TIMER(mrt->rs_timer); RESET_TIMER(mrt->assert_timer); RESET_TIMER(mrt->assert_rate_timer); mrt->kernel_cache = NULL; return mrt; } static mrtentry_t *create_mrtentry(srcentry_t *src, grpentry_t *grp, uint16_t flags) { mrtentry_t *node; mrtentry_t *grp_insert, *src_insert; /* pointers to insert */ uint32_t source; uint32_t group; if (flags & MRTF_SG) { /* (S,G) entry */ source = src->address; group = grp->group; if (search_grpmrtlink(grp, source, &grp_insert) == TRUE) return grp_insert; if (search_srcmrtlink(src, group, &src_insert) == TRUE) { /* Hmmm, search_grpmrtlink() didn't find the entry, but * search_srcmrtlink() did find it! Shouldn't happen. Panic! */ logit(LOG_ERR, 0, "MRT inconsistency for src %s and grp %s\n", inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2))); /* not reached but to make lint happy */ return NULL; } /* Create and insert in group mrtlink and source mrtlink chains. */ node = alloc_mrtentry(src, grp); if (!node) return NULL; /* node has to be insert right after grp_insert in the grp * mrtlink chain and right after src_insert in the src mrtlink * chain */ insert_grpmrtlink(node, grp_insert, grp); insert_srcmrtlink(node, src_insert, src); node->flags |= MRTF_SG; return node; } if (flags & MRTF_WC) { /* (*,G) entry */ if (grp->grp_route) return grp->grp_route; node = alloc_mrtentry(src, grp); if (!node) return NULL; grp->grp_route = node; node->flags |= (MRTF_WC | MRTF_RP); return node; } if (flags & MRTF_PMBR) { /* (*,*,RP) entry */ if (src->mrtlink) return src->mrtlink; node = alloc_mrtentry(src, grp); if (!node) return NULL; src->mrtlink = node; node->flags |= (MRTF_PMBR | MRTF_RP); return node; } return NULL; } /* * Delete all kernel cache for this mrtentry */ void delete_mrtentry_all_kernel_cache(mrtentry_t *mrt) { kernel_cache_t *prev; kernel_cache_t *node; if (!(mrt->flags & MRTF_KERNEL_CACHE)) return; /* Free all kernel_cache entries */ for (node = mrt->kernel_cache; node; ) { prev = node; node = node->next; logit(LOG_DEBUG, 0, "delete_mrtentry_all_kernel_cache: SG"); k_del_mfc(igmp_socket, prev->source, prev->group); free(prev); } mrt->kernel_cache = NULL; /* turn off the cache flag(s) */ mrt->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG); } void delete_single_kernel_cache(mrtentry_t *mrt, kernel_cache_t *node) { if (!mrt || !node) return; if (!node->prev) { mrt->kernel_cache = node->next; if (!mrt->kernel_cache) mrt->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG); } else { node->prev->next = node->next; } if (node->next) node->next->prev = node->prev; IF_DEBUG(DEBUG_MFC) { logit(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s", inet_fmt(node->source, s1, sizeof(s1)), inet_fmt(node->group, s2, sizeof(s2))); } logit(LOG_DEBUG, 0, "delete_single_kernel_cache: SG"); k_del_mfc(igmp_socket, node->source, node->group); free(node); } void delete_single_kernel_cache_addr(mrtentry_t *mrt, uint32_t source, uint32_t group) { uint32_t source_h; uint32_t group_h; kernel_cache_t *node; if (!mrt) return; source_h = ntohl(source); group_h = ntohl(group); /* Find the exact (S,G) kernel_cache entry */ for (node = mrt->kernel_cache; node; node = node->next) { if (ntohl(node->group) < group_h) continue; if (ntohl(node->group) > group_h) return; /* Not found */ if (ntohl(node->source) < source_h) continue; if (ntohl(node->source) > source_h) return; /* Not found */ /* Found exact match */ break; } if (!node) return; /* Found. Delete it */ if (!node->prev) { mrt->kernel_cache = node->next; if (!mrt->kernel_cache) mrt->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG); } else{ node->prev->next = node->next; } if (node->next) node->next->prev = node->prev; IF_DEBUG(DEBUG_MFC) { logit(LOG_DEBUG, 0, "Deleting MFC entry for source %s and group %s", inet_fmt(node->source, s1, sizeof(s1)), inet_fmt(node->group, s2, sizeof(s2))); } logit(LOG_DEBUG, 0, "delete_single_kernel_cache_addr: SG"); k_del_mfc(igmp_socket, node->source, node->group); free(node); } /* * Installs kernel cache for (source, group). Assumes mrt * is the correct entry. */ void add_kernel_cache(mrtentry_t *mrt, uint32_t source, uint32_t group, uint16_t flags) { uint32_t source_h; uint32_t group_h; kernel_cache_t *next; kernel_cache_t *prev; kernel_cache_t *node; if (!mrt) return; move_kernel_cache(mrt, flags); if (mrt->flags & MRTF_SG) { /* (S,G) */ if (mrt->flags & MRTF_KERNEL_CACHE) return; node = (kernel_cache_t *)calloc(1, sizeof(kernel_cache_t)); node->next = NULL; node->prev = NULL; node->source = source; node->group = group; node->sg_count.pktcnt = 0; node->sg_count.bytecnt = 0; node->sg_count.wrong_if = 0; mrt->kernel_cache = node; mrt->flags |= MRTF_KERNEL_CACHE; return; } source_h = ntohl(source); group_h = ntohl(group); prev = NULL; for (next = mrt->kernel_cache; next; prev = next, next = next->next) { if (ntohl(next->group) < group_h) continue; if (ntohl(next->group) > group_h) break; if (ntohl(next->source) < source_h) continue; if (ntohl(next->source) > source_h) break; /* Found exact match. Nothing to change. */ return; } /* The new entry must be placed between prev and * next */ node = calloc(1, sizeof(kernel_cache_t)); if (prev) prev->next = node; else mrt->kernel_cache = node; if (next) next->prev = node; node->prev = prev; node->next = next; node->source = source; node->group = group; node->sg_count.pktcnt = 0; node->sg_count.bytecnt = 0; node->sg_count.wrong_if = 0; mrt->flags |= MRTF_KERNEL_CACHE; } /* * Bring the kernel cache "UP": from the (*,*,RP) to (*,G) or (S,G) */ static void move_kernel_cache(mrtentry_t *mrt, uint16_t flags) { kernel_cache_t *node; kernel_cache_t *insert_node; kernel_cache_t *first_node; kernel_cache_t *last_node; kernel_cache_t *prev_node; mrtentry_t *mrtentry_pmbr; mrtentry_t *mrtentry_rp; uint32_t group_h; uint32_t source_h; int found; if (!mrt) return; if (mrt->flags & MRTF_PMBR) return; if (mrt->flags & MRTF_WC) { /* Move the cache info from (*,*,RP) to (*,G) */ group_h = ntohl(mrt->group->group); mrtentry_pmbr = mrt->group->active_rp_grp->rp->rpentry->mrtlink; if (!mrtentry_pmbr) return; /* Nothing to move */ first_node = last_node = NULL; for (node = mrtentry_pmbr->kernel_cache; node; node = node->next) { /* * The order is: (1) smaller group; * (2) smaller source within group */ if (ntohl(node->group) < group_h) continue; if (ntohl(node->group) != group_h) break; /* Select the kernel_cache entries to move */ if (!first_node) first_node = last_node = node; else last_node = node; } if (first_node) { /* Fix the old chain */ if (first_node->prev) first_node->prev->next = last_node->next; else mrtentry_pmbr->kernel_cache = last_node->next; if (last_node->next) last_node->next->prev = first_node->prev; if (!mrtentry_pmbr->kernel_cache) mrtentry_pmbr->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG); /* Insert in the new place */ prev_node = NULL; last_node->next = NULL; mrt->flags |= MRTF_KERNEL_CACHE; for (node = mrt->kernel_cache; node; ) { if (!first_node) break; /* All entries have been inserted */ if (ntohl(node->source) > ntohl(first_node->source)) { /* Insert the entry before node */ insert_node = first_node; first_node = first_node->next; if (node->prev) node->prev->next = insert_node; else mrt->kernel_cache = insert_node; insert_node->prev = node->prev; insert_node->next = node; node->prev = insert_node; } prev_node = node; node = node->next; } if (first_node) { /* Place all at the end after prev_node */ if (prev_node) prev_node->next = first_node; else mrt->kernel_cache = first_node; first_node->prev = prev_node; } } return; } if (mrt->flags & MRTF_SG) { logit(LOG_DEBUG, 0, "move_kernel_cache: SG"); /* (S,G) entry. Move the whole group cache from (*,*,RP) to (*,G) and * then get the necessary entry from (*,G). * TODO: Not optimized! The particular entry is moved first to (*,G), * then we have to search again (*,G) to find it and move to (S,G). */ /* TODO: XXX: No need for this? Thinking.... */ /* move_kernel_cache(mrt->group->grp_route, flags); */ mrtentry_rp = mrt->group->grp_route; if (!mrtentry_rp) { mrtentry_rp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; if (!mrtentry_rp) return; } if (mrtentry_rp->incoming != mrt->incoming) { /* XXX: the (*,*,RP) (or (*,G)) iif is different from the * (S,G) iif. No need to move the cache, because (S,G) don't * need it. After the first packet arrives on the shortest path, * the correct cache entry will be created. * If (flags & MFC_MOVE_FORCE) then we must move the cache. * This usually happens when switching to the shortest path. * The calling function will immediately call k_chg_mfc() * to modify the kernel cache. */ if (!(flags & MFC_MOVE_FORCE)) return; } /* Find the exact entry */ source_h = ntohl(mrt->source->address); group_h = ntohl(mrt->group->group); found = FALSE; for (node = mrtentry_rp->kernel_cache; node; node = node->next) { if (ntohl(node->group) < group_h) continue; if (ntohl(node->group) > group_h) break; if (ntohl(node->source) < source_h) continue; if (ntohl(node->source) > source_h) break; /* We found it! */ if (node->prev) node->prev->next = node->next; else mrtentry_rp->kernel_cache = node->next; if (node->next) node->next->prev = node->prev; found = TRUE; break; } if (found == TRUE) { if (!mrtentry_rp->kernel_cache) mrtentry_rp->flags &= ~(MRTF_KERNEL_CACHE | MRTF_MFC_CLONE_SG); if (mrt->kernel_cache) free(mrt->kernel_cache); mrt->flags |= MRTF_KERNEL_CACHE; mrt->kernel_cache = node; node->prev = NULL; node->next = NULL; } } } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/mrt.h000066400000000000000000000300011267035112600134170ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: mrt.h,v 1.16 2001/09/10 20:31:36 pavlin Exp $ */ #define MRTF_SPT 0x0001 /* iif toward source */ #define MRTF_WC 0x0002 /* (*,G) entry */ #define MRTF_RP 0x0004 /* iif toward RP */ #define MRTF_NEW 0x0008 /* new created routing entry */ #define MRTF_IIF_REGISTER 0x0020 /* ??? */ #define MRTF_REGISTER 0x0080 /* ??? */ #define MRTF_KERNEL_CACHE 0x0200 /* a mirror for the kernel cache */ #define MRTF_NULL_OIF 0x0400 /* null oif cache.. ??? */ #define MRTF_REG_SUPP 0x0800 /* register suppress ??? */ #define MRTF_ASSERTED 0x1000 /* upstream is not that of src ??? */ #define MRTF_SG 0x2000 /* (S,G) pure, not hanging off of (*,G)*/ #define MRTF_PMBR 0x4000 /* (*,*,RP) entry (for interop) */ #define MRTF_MFC_CLONE_SG 0x8000 /* clone (S,G) MFC from (*,G) or (*,*,RP) */ /* Macro to duplicate oif info (oif bits, timers) */ #define VOIF_COPY(from, to) \ do { \ VIFM_COPY((from)->joined_oifs, (to)->joined_oifs); \ VIFM_COPY((from)->oifs, (to)->oifs); \ VIFM_COPY((from)->leaves, (to)->leaves); \ VIFM_COPY((from)->pruned_oifs, (to)->pruned_oifs); \ VIFM_COPY((from)->asserted_oifs, (to)->asserted_oifs); \ memcpy((to)->vif_timers, (from)->vif_timers, \ numvifs * sizeof((from)->vif_timers[0])); \ memcpy((to)->vif_deletion_delay, (from)->vif_deletion_delay, \ numvifs * sizeof((from)->vif_deletion_delay[0])); \ } while (0) #define FREE_MRTENTRY(mrtentry_ptr) \ do { \ kernel_cache_t *curr; \ kernel_cache_t *next; \ \ if ((mrtentry_ptr)->vif_timers) \ free((mrtentry_ptr)->vif_timers); \ if ((mrtentry_ptr)->vif_deletion_delay) \ free((mrtentry_ptr)->vif_deletion_delay); \ curr = (mrtentry_ptr)->kernel_cache; \ while (curr) { \ next = curr->next; \ free(curr); \ curr = next; \ } \ free(mrtentry_ptr); \ } while (0) /* * The complicated structure used by the more complicated Join/Prune * message building */ typedef struct build_jp_message_ { struct build_jp_message_ *next; /* Used to chain the free entries */ uint8_t *jp_message; /* The Join/Prune message */ uint32_t jp_message_size; /* Size of the Join/Prune message (in bytes) */ uint16_t holdtime; /* Join/Prune message holdtime field */ uint32_t curr_group; /* Current group address */ uint8_t curr_group_msklen;/* Current group masklen */ uint8_t *join_list; /* The working area for the join addresses */ uint32_t join_list_size; /* The size of the join_list (in bytes) */ uint16_t join_addr_number; /* Number of the join addresses in join_list */ uint8_t *prune_list; /* The working area for the prune addresses */ uint32_t prune_list_size; /* The size of the prune_list (in bytes) */ uint16_t prune_addr_number;/* Number of the prune addresses in prune_list*/ uint8_t *rp_list_join; /* The working area for RP join addresses */ uint32_t rp_list_join_size;/* The size of the rp_list_join (in bytes) */ uint16_t rp_list_join_number;/* Number of RP addresses in rp_list_join */ uint8_t *rp_list_prune; /* The working area for RP prune addresses */ uint32_t rp_list_prune_size;/* The size of the rp_list_prune (in bytes) */ uint16_t rp_list_prune_number;/* Number of RP addresses in rp_list_prune */ uint8_t *num_groups_ptr; /* Pointer to number_of_groups in jp_message */ } build_jp_message_t; typedef struct pim_nbr_entry { struct pim_nbr_entry *next; /* link to next neighbor */ struct pim_nbr_entry *prev; /* link to prev neighbor */ uint32_t address; /* neighbor address */ int8_t dr_prio_present;/* If set, this neighbor has prio */ uint32_t dr_prio; /* DR priority: 1 (default) */ uint32_t genid; /* Cached generation ID */ vifi_t vifi; /* which interface */ uint16_t timer; /* for timing out neighbor */ build_jp_message_t *build_jp_message; /* A structure for fairly * complicated Join/Prune * message construction. */ } pim_nbr_entry_t; typedef struct srcentry { struct srcentry *next; /* link to next entry */ struct srcentry *prev; /* link to prev entry */ uint32_t address; /* source or RP address */ struct mrtentry *mrtlink; /* link to routing entries */ vifi_t incoming; /* incoming vif */ struct pim_nbr_entry *upstream; /* upstream router */ uint32_t metric; /* Unicast Routing Metric to the source */ uint32_t preference; /* The metric preference (for assers)*/ uint16_t timer; /* Entry timer??? Delete? */ struct cand_rp *cand_rp; /* Used if this is rpentry_t */ } srcentry_t; typedef srcentry_t rpentry_t; /* (RP<->group) matching table related structures */ typedef struct cand_rp { struct cand_rp *next; /* Next candidate RP */ struct cand_rp *prev; /* Previous candidate RP */ struct rp_grp_entry *rp_grp_next; /* The rp_grp_entry chain for that RP*/ rpentry_t *rpentry; /* Pointer to the RP entry */ } cand_rp_t; typedef struct grp_mask { struct grp_mask *next; struct grp_mask *prev; struct rp_grp_entry *grp_rp_next; uint32_t group_addr; uint32_t group_mask; uint32_t hash_mask; uint16_t fragment_tag; /* Used for garbage collection */ uint8_t group_rp_number; /* Used when assembling segments */ } grp_mask_t; typedef struct rp_grp_entry { struct rp_grp_entry *rp_grp_next; /* Next entry for same RP */ struct rp_grp_entry *rp_grp_prev; /* Prev entry for same RP */ struct rp_grp_entry *grp_rp_next; /* Next entry for same grp prefix */ struct rp_grp_entry *grp_rp_prev; /* Prev entry for same grp prefix */ struct grpentry *grplink; /* Link to all grps via this entry */ uint16_t holdtime; /* The RP holdtime */ uint16_t fragment_tag; /* The fragment tag from the * received BSR message */ uint8_t priority; /* The RP priority */ grp_mask_t *group; /* Pointer to (group,mask) entry */ cand_rp_t *rp; /* Pointer to the RP */ } rp_grp_entry_t; typedef struct grpentry { struct grpentry *next; /* link to next entry */ struct grpentry *prev; /* link to prev entry */ struct grpentry *rpnext; /* next grp for the same RP */ struct grpentry *rpprev; /* prev grp for the same RP */ uint32_t group; /* subnet group of multicasts */ uint32_t rpaddr; /* The IP address of the RP */ struct mrtentry *mrtlink; /* link to (S,G) routing entries */ rp_grp_entry_t *active_rp_grp;/* Pointer to the active rp_grp entry*/ struct mrtentry *grp_route; /* Pointer to the (*,G) routing entry*/ } grpentry_t; typedef struct mrtentry { struct mrtentry *grpnext; /* next entry of same group */ struct mrtentry *grpprev; /* prev entry of same group */ struct mrtentry *srcnext; /* next entry of same source */ struct mrtentry *srcprev; /* prev entry of same source */ struct grpentry *group; /* pointer to group entry */ struct srcentry *source; /* pointer to source entry (or RP) */ vifi_t incoming; /* the iif (either toward S or RP) */ vifbitmap_t oifs; /* The current result oifs */ vifbitmap_t joined_oifs; /* The joined oifs (Join received) */ vifbitmap_t pruned_oifs; /* The pruned oifs (Prune received) */ vifbitmap_t asserted_oifs;/* The asserted oifs (lost Assert) */ vifbitmap_t leaves; /* Has directly connected members */ struct pim_nbr_entry *upstream; /* upstream router, needed because * of the asserts it may be different * than the source (or RP) upstream * router. */ uint32_t metric; /* Routing Metric for this entry */ uint32_t preference; /* The metric preference value */ uint32_t pmbr_addr; /* The PMBR address (for interop) */ uint16_t *vif_timers; /* vifs timer list */ uint16_t *vif_deletion_delay; /* vifs deletion delay list */ uint16_t flags; /* The MRTF_* flags */ uint16_t timer; /* entry timer */ uint16_t jp_timer; /* The Join/Prune timer */ uint16_t rs_timer; /* Register-Suppression Timer */ u_int assert_timer; u_int assert_rate_timer; struct kernel_cache *kernel_cache; /* List of the kernel cache entries */ #ifdef RSRR struct rsrr_cache *rsrr_cache; /* Used to save RSRR requests for * route change notification. */ #endif /* RSRR */ } mrtentry_t; /* * Used to get forwarded data related counts (number of packet, number of * bits, etc) */ struct sg_count { uint32_t pktcnt; /* Number of packets for (s,g) */ uint32_t bytecnt; /* Number of bytes for (s,g) */ uint32_t wrong_if; /* Number of packets received on wrong iif for (s,g) */ }; struct vif_count { uint32_t icount; /* Input packet count on vif */ uint32_t ocount; /* Output packet count on vif */ uint32_t ibytes; /* Input byte count on vif */ uint32_t obytes; /* Output byte count on vif */ }; /* * Structure to keep track of existing (S,G) MFC entries in the kernel * for particular (*,G) or (*,*,RP) entry. We must keep track for * each active source which doesn't have (S,G) entry in the daemon's * routing table. We need to keep track of such sources for two reasons: * * (1) If the kernel does not support (*,G) MFC entries (currently, the * "official" mcast code doesn't), we must know all installed (s,G) entries * in the kernel and modify them if the iif or oif for the (*,G) changes. * * (2) By checking periodically the traffic coming from the shared tree, * we can either delete the idle sources or switch to the shortest path. * * Note that even if we have (*,G) implemented in the kernel, we still * need to have this structure because of (2) */ typedef struct kernel_cache { struct kernel_cache *next; struct kernel_cache *prev; uint32_t source; uint32_t group; struct sg_count sg_count; /* The (s,g) data retated counters (see above) */ } kernel_cache_t; /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/netlink.c000066400000000000000000000144071267035112600142700ustar00rootroot00000000000000/* * Fred Griffoul sent me this file to use * when compiling pimd under Linux. * * There was no copyright message or author name, so I assume he was the * author, and deserves the copyright/credit for it: * * COPYRIGHT/AUTHORSHIP by Fred Griffoul * (until proven otherwise). */ #include #include #include #include #include #include #include #include "defs.h" #include int routing_socket = -1; static uint32_t pid; /* pid_t, but /usr/include/linux/netlink.h says __u32 ... */ static uint32_t seq; static int getmsg(struct rtmsg *rtm, int msglen, struct rpfctl *rpf); static int addattr32(struct nlmsghdr *n, size_t maxlen, int type, uint32_t data) { int len = RTA_LENGTH(4); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) return -1; rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), &data, 4); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; return 0; } static int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) { while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta, len); } if (len) logit(LOG_WARNING, 0, "NETLINK: Deficit in rtattr %d", len); return 0; } /* open and initialize the routing socket */ int init_routesock(void) { socklen_t addr_len; struct sockaddr_nl local; routing_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (routing_socket < 0) { logit(LOG_ERR, errno, "Failed creating netlink socket"); return -1; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = 0; if (bind(routing_socket, (struct sockaddr *)&local, sizeof(local)) < 0) { logit(LOG_ERR, errno, "Failed binding to netlink socket"); return -1; } addr_len = sizeof(local); if (getsockname(routing_socket, (struct sockaddr *)&local, &addr_len) < 0) { logit(LOG_ERR, errno, "Failed netlink getsockname"); return -1; } if (addr_len != sizeof(local)) { logit(LOG_ERR, 0, "Invalid netlink addr len."); return -1; } if (local.nl_family != AF_NETLINK) { logit(LOG_ERR, 0, "Invalid netlink addr family."); return -1; } pid = local.nl_pid; seq = time(NULL); return 0; } /* get the rpf neighbor info */ int k_req_incoming(uint32_t source, struct rpfctl *rpf) { int l, rlen; char buf[512]; struct nlmsghdr *n = (struct nlmsghdr *)buf; struct rtmsg *r = NLMSG_DATA(n); struct sockaddr_nl addr; rpf->source.s_addr = source; rpf->iif = ALL_VIFS; rpf->rpfneighbor.s_addr = 0; n->nlmsg_type = RTM_GETROUTE; n->nlmsg_flags = NLM_F_REQUEST; n->nlmsg_len = NLMSG_LENGTH(sizeof(*r)); n->nlmsg_pid = pid; n->nlmsg_seq = ++seq; memset(r, 0, sizeof(*r)); r->rtm_family = AF_INET; r->rtm_dst_len = 32; addattr32(n, sizeof(buf), RTA_DST, rpf->source.s_addr); #ifdef CONFIG_RTNL_OLD_IFINFO r->rtm_optlen = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); #endif addr.nl_family = AF_NETLINK; addr.nl_groups = 0; addr.nl_pid = 0; if (!IN_LINK_LOCAL_RANGE(rpf->source.s_addr)) { IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "NETLINK: ask path to %s", inet_fmt(rpf->source.s_addr, s1, sizeof(s1))); } while ((rlen = sendto(routing_socket, buf, n->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr))) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_WARNING, errno, "Error writing to routing socket"); return FALSE; } do { socklen_t alen = sizeof(addr); l = recvfrom(routing_socket, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &alen); if (l < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_WARNING, errno, "Error writing to routing socket"); return FALSE; } } while (n->nlmsg_seq != seq || n->nlmsg_pid != pid); if (n->nlmsg_type != RTM_NEWROUTE) { if (!IN_LINK_LOCAL_RANGE(rpf->source.s_addr)) { if (n->nlmsg_type != NLMSG_ERROR) logit(LOG_WARNING, 0, "netlink: wrong answer type %d", n->nlmsg_type); else logit(LOG_WARNING, -(*(int*)NLMSG_DATA(n)), "netlink get_route"); } return FALSE; } return getmsg(NLMSG_DATA(n), l - sizeof(*n), rpf); } static int getmsg(struct rtmsg *rtm, int msglen, struct rpfctl *rpf) { int ifindex; vifi_t vifi; struct uvif *v; struct rtattr *rta[RTA_MAX + 1]; if (!rpf) { logit(LOG_WARNING, 0, "Missing rpf pointer to netlink.c:getmsg()!"); return FALSE; } rpf->iif = NO_VIF; rpf->rpfneighbor.s_addr = INADDR_ANY; if (rtm->rtm_type == RTN_LOCAL) { IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "NETLINK: local address"); if ((rpf->iif = local_address(rpf->source.s_addr)) != MAXVIFS) { rpf->rpfneighbor.s_addr = rpf->source.s_addr; return TRUE; } return FALSE; } if (rtm->rtm_type != RTN_UNICAST) { IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "NETLINK: route type is %d", rtm->rtm_type); return FALSE; } memset(rta, 0, sizeof(rta)); parse_rtattr(rta, RTA_MAX, RTM_RTA(rtm), msglen - sizeof(*rtm)); if (!rta[RTA_OIF]) { logit(LOG_WARNING, 0, "NETLINK: no outbound interface"); return FALSE; } /* Get ifindex of outbound interface */ ifindex = *(int *)RTA_DATA(rta[RTA_OIF]); for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_ifindex == ifindex) break; } if (vifi >= numvifs) { logit(LOG_WARNING, 0, "NETLINK: ifindex=%d, but no vif", ifindex); return FALSE; } /* Found inbound interface in vifi */ rpf->iif = vifi; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "NETLINK: vif %d, ifindex=%d", vifi, ifindex); if (rta[RTA_GATEWAY]) { uint32_t gw = *(uint32_t *)RTA_DATA(rta[RTA_GATEWAY]); IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "NETLINK: gateway is %s", inet_fmt(gw, s1, sizeof(s1))); rpf->rpfneighbor.s_addr = gw; } else { rpf->rpfneighbor.s_addr = rpf->source.s_addr; } return TRUE; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/pathnames.h000066400000000000000000000043761267035112600146150ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #ifndef PIMD_PATHNAMES_H_ #define PIMD_PATHNAMES_H_ #include #ifndef _PATH_SYSCONF #define _PATH_SYSCONF "/etc" #endif #define _PATH_PIMD_CONF _PATH_SYSCONF "/pimd.conf" #define _PATH_PIMD_RUNDIR _PATH_VARRUN "pimd" #define _PATH_PIMD_DUMP _PATH_PIMD_RUNDIR "/pimd.dump" #define _PATH_PIMD_CACHE _PATH_PIMD_RUNDIR "/pimd.cache" #endif /* PIMD_PATHNAMES_H_ */ pimd-2.3.2/pim.c000066400000000000000000000417501267035112600134120ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: pim.c,v 1.24 2002/09/26 00:59:30 pavlin Exp $ */ #include "defs.h" #define SEND_DEBUG_NUMBER 50 /* For throttling log messages */ /* * Exported variables. */ char *pim_recv_buf; /* input packet buffer */ char *pim_send_buf; /* output packet buffer */ uint32_t allpimrouters_group; /* ALL_PIM_ROUTERS address in net order */ int pim_socket; /* socket for PIM control msgs */ #ifdef RAW_OUTPUT_IS_RAW extern int curttl; #endif /* RAW_OUTPUT_IS_RAW */ /* * Local variables. */ static uint16_t ip_id = 0; //static u_int pim_send_cnt = 0; /* * Local function definitions. */ static void pim_read (int f, fd_set *rfd); static void accept_pim (ssize_t recvlen); static int send_frame (char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen); /* * Setup raw kernel socket for PIM protocol and send/receive buffers. */ void init_pim(void) { struct ip *ip; /* Setup the PIM raw socket */ if ((pim_socket = socket(AF_INET, SOCK_RAW, IPPROTO_PIM)) < 0) logit(LOG_ERR, errno, "Failed creating PIM socket"); k_hdr_include(pim_socket, TRUE); /* include IP header when sending */ k_set_sndbuf(pim_socket, SO_SEND_BUF_SIZE_MAX, SO_SEND_BUF_SIZE_MIN); /* lots of output buffering */ k_set_rcvbuf(pim_socket, SO_RECV_BUF_SIZE_MAX, SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */ k_set_ttl(pim_socket, MINTTL); /* restrict multicasts to one hop */ k_set_loop(pim_socket, FALSE); /* disable multicast loopback */ allpimrouters_group = htonl(INADDR_ALL_PIM_ROUTERS); pim_recv_buf = calloc(1, RECV_BUF_SIZE); pim_send_buf = calloc(1, SEND_BUF_SIZE); if (!pim_recv_buf || !pim_send_buf) logit(LOG_ERR, 0, "Ran out of memory in init_pim()"); /* One time setup in the buffers */ ip = (struct ip *)pim_send_buf; memset(ip, 0, sizeof(*ip)); ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) >> 2); ip->ip_tos = 0; /* TODO: setup?? */ ip->ip_id = 0; /* Make sure to update ID field, maybe fragmenting below */ ip->ip_off = 0; ip->ip_p = IPPROTO_PIM; ip->ip_sum = 0; /* let kernel fill in */ if (register_input_handler(pim_socket, pim_read) < 0) logit(LOG_ERR, 0, "Failed registering pim_read() as an input handler"); /* Initialize the building Join/Prune messages working area */ build_jp_message_pool = (build_jp_message_t *)NULL; build_jp_message_pool_counter = 0; } /* Read a PIM message */ static void pim_read(int f __attribute__((unused)), fd_set *rfd __attribute__((unused))) { ssize_t len; socklen_t dummy = 0; sigset_t block, oblock; while ((len = recvfrom(pim_socket, pim_recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_ERR, errno, "Failed recvfrom() in pim_read()"); return; } sigemptyset(&block); sigaddset(&block, SIGALRM); if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0) logit(LOG_ERR, errno, "sigprocmask"); accept_pim(len); sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL); } static void accept_pim(ssize_t recvlen) { uint32_t src, dst; struct ip *ip; pim_header_t *pim; int iphdrlen, pimlen; char source[20], dest[20]; if (recvlen < (ssize_t)sizeof(struct ip)) { logit(LOG_WARNING, 0, "Received PIM packet too short (%u bytes) for IP header", recvlen); return; } ip = (struct ip *)pim_recv_buf; src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; iphdrlen = ip->ip_hl << 2; pim = (pim_header_t *)(pim_recv_buf + iphdrlen); pimlen = recvlen - iphdrlen; /* Sanity check packet length */ if (pimlen < (ssize_t)sizeof(*pim)) { logit(LOG_WARNING, 0, "IP data field too short (%d bytes) for PIM header, from %s to %s", pimlen, inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); return; } IF_DEBUG(DEBUG_PIM_DETAIL) { IF_DEBUG(DEBUG_PIM) { logit(LOG_DEBUG, 0, "RECV %5d bytes %s from %-15s to %s ", recvlen, packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); } } /* TODO: Check PIM version */ /* TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address) */ /* TODO: Checksum verification is done in each of the processing functions. * No need for checksum, if already done in the kernel? */ switch (pim->pim_type) { case PIM_HELLO: receive_pim_hello(src, dst, (char *)(pim), pimlen); break; case PIM_REGISTER: receive_pim_register(src, dst, (char *)(pim), pimlen); break; case PIM_REGISTER_STOP: receive_pim_register_stop(src, dst, (char *)(pim), pimlen); break; case PIM_JOIN_PRUNE: receive_pim_join_prune(src, dst, (char *)(pim), pimlen); break; case PIM_BOOTSTRAP: receive_pim_bootstrap(src, dst, (char *)(pim), pimlen); break; case PIM_ASSERT: receive_pim_assert(src, dst, (char *)(pim), pimlen); break; case PIM_GRAFT: case PIM_GRAFT_ACK: logit(LOG_INFO, 0, "ignore %s from %s to %s", packet_kind(IPPROTO_PIM, pim->pim_type, 0), inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); break; case PIM_CAND_RP_ADV: receive_pim_cand_rp_adv(src, dst, (char *)(pim), pimlen); break; default: logit(LOG_INFO, 0, "ignore unknown PIM message code %u from %s to %s", pim->pim_type, inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); break; } } /* * Send a multicast PIM packet from src to dst, PIM message type = "type" * and data length (after the PIM header) = "len" */ void send_pim(char *buf, uint32_t src, uint32_t dst, int type, size_t len) { struct sockaddr_in sin; struct ip *ip; pim_header_t *pim; int sendlen = sizeof(struct ip) + sizeof(pim_header_t) + len; int setloop = 0; char source[20], dest[20]; /* Prepare the IP header */ ip = (struct ip *)buf; ip->ip_id = htons(++ip_id); ip->ip_off = 0; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; ip->ip_ttl = MAXTTL; /* applies to unicast only */ #ifdef HAVE_IP_HDRINCL_BSD_ORDER ip->ip_len = sendlen; #else ip->ip_len = htons(sendlen); #endif /* Prepare the PIM packet */ pim = (pim_header_t *)(buf + sizeof(struct ip)); pim->pim_type = type; pim->pim_vers = PIM_PROTOCOL_VERSION; pim->pim_reserved = 0; pim->pim_cksum = 0; /* TODO: XXX: if start using this code for PIM_REGISTERS, exclude the * encapsulated packet from the checsum. */ pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len); if (IN_MULTICAST(ntohl(dst))) { k_set_if(pim_socket, src); if ((dst == allhosts_group) || (dst == allrouters_group) || (dst == allpimrouters_group) || (dst == allreports_group)) { setloop = 1; k_set_loop(pim_socket, TRUE); } #ifdef RAW_OUTPUT_IS_RAW ip->ip_ttl = curttl; } else { ip->ip_ttl = MAXTTL; #endif /* RAW_OUTPUT_IS_RAW */ } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = dst; #ifdef HAVE_SA_LEN sin.sin_len = sizeof(sin); #endif while (sendto(pim_socket, buf, sendlen, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ if (errno == ENETDOWN || errno == ENODEV) check_vif_state(); else if (errno == EPERM || errno == EHOSTUNREACH) logit(LOG_WARNING, 0, "Not allowed to send PIM message from %s to %s, possibly firewall" #ifdef __linux__ ", or SELinux policy violation," #endif " related problem." , inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); else logit(LOG_WARNING, errno, "sendto from %s to %s", inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); if (setloop) k_set_loop(pim_socket, FALSE); return; } if (setloop) k_set_loop(pim_socket, FALSE); IF_DEBUG(DEBUG_PIM_DETAIL) { IF_DEBUG(DEBUG_PIM) { logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", sendlen, packet_kind(IPPROTO_PIM, type, 0), src == INADDR_ANY_N ? "INADDR_ANY" : inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); } } } /* TODO: This can be merged with the above procedure */ /* * Send an unicast PIM packet from src to dst, PIM message type = "type" * and data length (after the PIM common header) = "len" */ void send_pim_unicast(char *buf, int mtu, uint32_t src, uint32_t dst, int type, size_t len) { struct sockaddr_in sin; struct ip *ip; pim_header_t *pim; int result, sendlen = sizeof(struct ip) + sizeof(pim_header_t) + len; char source[20], dest[20]; /* Prepare the IP header */ ip = (struct ip *)buf; ip->ip_id = htons(++ip_id); ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; ip->ip_ttl = MAXTTL; /* TODO: XXX: setup TTL from the inner mcast packet? */ #ifdef HAVE_IP_HDRINCL_BSD_ORDER ip->ip_len = sendlen; #else ip->ip_len = htons(sendlen); #endif /* Prepare the PIM packet */ pim = (pim_header_t *)(buf + sizeof(struct ip)); pim->pim_type = type; pim->pim_vers = PIM_PROTOCOL_VERSION; pim->pim_reserved = 0; pim->pim_cksum = 0; /* XXX: The PIM_REGISTERs don't include the encapsulated * inner packet in the checksum. * Well, try to explain this to cisco... * If your RP is cisco and if it shows many PIM_REGISTER checksum * errors from this router, then #define BROKEN_CISCO_CHECKSUM here * or in your Makefile. * Note that such checksum is not in the spec, and such PIM_REGISTERS * may be dropped by some implementations (pimd should be OK). */ #ifdef BROKEN_CISCO_CHECKSUM pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len); #else if (PIM_REGISTER == type) { pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + sizeof(pim_register_t)); } else { pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len); } #endif /* BROKEN_CISCO_CHECKSUM */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = dst; #ifdef HAVE_SA_LEN sin.sin_len = sizeof(sin); #endif IF_DEBUG(DEBUG_PIM_DETAIL) { IF_DEBUG(DEBUG_PIM) { logit(LOG_DEBUG, 0, "SEND %5d bytes %s from %-15s to %s ...", sendlen, packet_kind(IPPROTO_PIM, type, 0), inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); } } result = send_frame(buf, sendlen, 0, mtu, (struct sockaddr *)&sin, sizeof(sin)); if (result) { logit(LOG_WARNING, errno, "sendto from %s to %s", inet_fmt(ip->ip_src.s_addr, source, sizeof(source)), inet_fmt(ip->ip_dst.s_addr, dest, sizeof(dest))); return; } IF_DEBUG(DEBUG_PIM_DETAIL) { IF_DEBUG(DEBUG_PIM) { #if 0 /* TODO: use pim_send_cnt? */ if (++pim_send_cnt > SEND_DEBUG_NUMBER) { pim_send_cnt = 0; logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", sendlen, packet_kind(IPPROTO_PIM, type, 0), inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); } #endif logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", sendlen, packet_kind(IPPROTO_PIM, type, 0), inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest))); } } } #if 1 /* * send unicast register frames * Version: Michael Fine * Staus: Works, albeit non-optimal * Design: Only fragments if sendto() fails with EMSGSIZE * It then tries to re-send by splitting the frame in two equal halves, * calling send_frame() recursively until the frame has been sent. */ static int send_frame(char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen) { struct ip *ip = (struct ip *)buf; char source[20], dest[20]; IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_INFO, 0, "Sending unicast: len = %d, frag %zd, mtu %zd, to %s", len, frag, mtu, inet_fmt(ip->ip_dst.s_addr, source, sizeof(source))); dump_frame(NULL, buf, len); } while (sendto(pim_socket, buf, len, 0, dst, salen) < 0) { switch (errno) { case EINTR: continue; /* Received signal, retry syscall. */ case ENETDOWN: check_vif_state(); return -1; case EMSGSIZE: { /* split it in half and recursively send each half */ size_t hdrsize = sizeof(struct ip); size_t newlen1 = ((len - hdrsize) / 2) & 0xFFF8; /* 8 byte boundary */ size_t sendlen = newlen1 + hdrsize; size_t offset = ntohs(ip->ip_off); /* send first half */ ip->ip_len = htons(sendlen); ip->ip_off = htons(offset | IP_MF); if (send_frame(buf, sendlen, 1, newlen1, dst, salen) == 0) { /* send second half */ struct ip *ip2 = (struct ip *)(buf + newlen1); size_t newlen2 = len - sendlen; sendlen = newlen2 + hdrsize; memcpy(ip2, ip, hdrsize); ip2->ip_len = htons(sendlen); ip2->ip_off = htons(offset + (newlen1 >> 3)); /* keep flgs */ return send_frame((char *)ip2, sendlen, 1, newlen2, dst, salen); } return -1; } default: logit(LOG_WARNING, errno, "sendto from %s to %s", inet_fmt(ip->ip_src.s_addr, source, sizeof(source)), inet_fmt(ip->ip_dst.s_addr, dest, sizeof(dest))); return -1; } } return 0; } #else /* * send unicast register frames * Version: Joachim Nilsson * Staus: Does not work (yet!) * Design: Fragment IP frames when the frame length exceeds the MTU * reported from the interface. Optimizes for less fragments * and fewer syscalls, should get better network utilization. * Can be easily modified to use the PMTU instead. * * Feel free to debug this version and submit your patches -- it should work! --Joachim */ static int send_frame(char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen) { struct ip *next, *ip = (struct ip *)buf; size_t xferlen, offset; char source[20], dest[20]; if (!mtu) mtu = IP_MSS; if (len > mtu) xferlen = (mtu - sizeof(struct ip)) & 0xFFF8; else xferlen = len; offset = (ntohs(ip->ip_off) & IP_OFFMASK) + (frag >> 3); ip->ip_off = offset; len = len - xferlen; if (len) ip->ip_off |= IP_MF; ip->ip_off = htons(ip->ip_off); ip->ip_len = htons(xferlen); IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_INFO, 0, "Sending %-4d bytes %sunicast (MTU %-4d, offset %zd) to %s", xferlen, len ? "fragmented " : "", mtu, offset, inet_fmt(ip->ip_dst.s_addr, source, sizeof(source))); dump_frame(NULL, buf, xferlen); } /* send first fragment */ while (sendto(pim_socket, ip, xferlen, 0, dst, salen) < 0) { switch (errno) { case EINTR: continue; /* Received signal, retry syscall. */ case ENETDOWN: check_vif_state(); return -1; case EMSGSIZE: if (mtu > IP_MSS) return send_frame((char *)ip, xferlen, frag, IP_MSS, dst, salen); /* fall through */ default: return -1; } } /* send reminder */ if (len) { size_t hdrsz = sizeof(struct ip); /* Update data pointers */ next = (struct ip *)(buf + xferlen - hdrsz); memcpy(next, ip, hdrsz); return send_frame((char *)next, len + hdrsz, xferlen, mtu, dst, salen); } return 0; } #endif /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/pim_proto.c000066400000000000000000003357731267035112600146500ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: pim_proto.c,v 1.47 2003/05/28 22:57:16 pavlin Exp $ */ #include #include "defs.h" typedef struct { uint16_t holdtime; uint32_t dr_prio; int8_t dr_prio_present; uint32_t genid; } pim_hello_opts_t; /* * Local functions definitions. */ static int restart_dr_election (struct uvif *v); static int parse_pim_hello (char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts); static void cache_nbr_settings (pim_nbr_entry_t *nbr, pim_hello_opts_t *opts); static int send_pim_register_stop (uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_source); static build_jp_message_t *get_jp_working_buff (void); static void return_jp_working_buff (pim_nbr_entry_t *pim_nbr); static void pack_jp_message (pim_nbr_entry_t *pim_nbr); static void send_jp_message (pim_nbr_entry_t *pim_nbr); static int compare_metrics (uint32_t local_preference, uint32_t local_metric, uint32_t local_address, uint32_t remote_preference, uint32_t remote_metric, uint32_t remote_address); build_jp_message_t *build_jp_message_pool; int build_jp_message_pool_counter; /************************************************************************ * PIM_HELLO ************************************************************************/ int receive_pim_hello(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len) { vifi_t vifi; struct uvif *v; size_t bsr_length; pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr; pim_hello_opts_t opts; srcentry_t *srcentry; mrtentry_t *mrtentry; if (inet_cksum((uint16_t *)msg, len)) return FALSE; vifi = find_vif_direct(src); if (vifi == NO_VIF) { /* Either a local vif or somehow received PIM_HELLO from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) logit(LOG_DEBUG, 0, "Ignoring PIM_HELLO from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } v = &uvifs[vifi]; if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ /* Get the Holdtime (in seconds) and any DR priority from the message. Return if error. */ if (parse_pim_hello(msg, len, src, &opts) == FALSE) return FALSE; IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u", inet_fmt(src, s1, sizeof(s1)), opts.holdtime); IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "PIM DR PRIORITY from %s is %u", inet_fmt(src, s1, sizeof(s1)), opts.dr_prio); IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "PIM GenID from %s is %u", inet_fmt(src, s1, sizeof(s1)), opts.genid); for (prev_nbr = NULL, nbr = v->uv_pim_neighbors; nbr; prev_nbr = nbr, nbr = nbr->next) { /* The PIM neighbors are sorted in decreasing order of the * network addresses (note that to be able to compare them * correctly we must translate the addresses in host order. */ if (ntohl(src) < ntohl(nbr->address)) continue; if (src == nbr->address) { /* We already have an entry for this host */ if (0 == opts.holdtime) { /* Looks like we have a nice neighbor who is going down * and wants to inform us by sending "holdtime=0". Thanks * buddy and see you again! */ logit(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down", inet_fmt(src, s1, sizeof(s1))); delete_pim_nbr(nbr); return TRUE; } /* https://tools.ietf.org/html/draft-ietf-pim-hello-genid-01 */ if (nbr->genid != opts.genid) { /* Known neighbor rebooted, update info and resend RP-Set */ cache_nbr_settings(nbr, &opts); goto rebooted; } if (nbr->dr_prio != opts.dr_prio) { /* New DR priority for neighbor, restart DR election */ cache_nbr_settings(nbr, &opts); goto election; } cache_nbr_settings(nbr, &opts); return TRUE; } /* No entry for this neighbor. Exit loop to create an entry for it. */ break; } /* * This is a new neighbor. Create a new entry for it. * It must be added right after `prev_nbr` */ IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_INFO, 0, "Received PIM HELLO from new neighbor %s", inet_fmt(src, s1, sizeof(s1))); new_nbr = calloc(1, sizeof(pim_nbr_entry_t)); if (!new_nbr) logit(LOG_ERR, 0, "Ran out of memory in receive_pim_hello()"); new_nbr->address = src; new_nbr->vifi = vifi; new_nbr->build_jp_message = NULL; new_nbr->next = nbr; new_nbr->prev = prev_nbr; /* Add PIM Hello options */ cache_nbr_settings(new_nbr, &opts); /* Add to linked list of neighbors */ if (prev_nbr) prev_nbr->next = new_nbr; else v->uv_pim_neighbors = new_nbr; if (new_nbr->next) new_nbr->next->prev = new_nbr; v->uv_flags &= ~VIFF_NONBRS; v->uv_flags |= VIFF_PIM_NBR; /* Since a new neighbour has come up, let it know your existence */ /* XXX: TODO: not in the spec, * but probably should send the message after a short random period? */ send_pim_hello(v, pim_timer_hello_holdtime); rebooted: if (v->uv_flags & VIFF_DR) { /* * If I am the current DR on that interface, so * send an RP-Set message to the new neighbor. */ if ((bsr_length = create_pim_bootstrap_message(pim_send_buf))) send_pim_unicast(pim_send_buf, v->uv_mtu, v->uv_lcl_addr, src, PIM_BOOTSTRAP, bsr_length); } election: if (restart_dr_election(v)) { /* I was the DR, but not anymore. Remove all register_vif from * oif list for all directly connected sources (for vifi). */ /* TODO: XXX: first entry is not used! */ for (srcentry = srclist->next; srcentry; srcentry = srcentry->next) { /* If not directly connected source for vifi */ if ((srcentry->incoming != vifi) || srcentry->upstream) continue; for (mrtentry = srcentry->mrtlink; mrtentry; mrtentry = mrtentry->srcnext) { if (!(mrtentry->flags & MRTF_SG)) continue; /* This is not (S,G) entry */ /* Remove the register oif */ VIFM_CLR(reg_vif_num, mrtentry->joined_oifs); change_interfaces(mrtentry, mrtentry->incoming, mrtentry->joined_oifs, mrtentry->pruned_oifs, mrtentry->leaves, mrtentry->asserted_oifs, 0); } } } /* * TODO: XXX: does a new neighbor change any routing entries info? * Need to trigger joins? */ IF_DEBUG(DEBUG_PIM_HELLO) dump_vifs(stderr); /* Show we got a new neighbor */ return TRUE; } void delete_pim_nbr(pim_nbr_entry_t *nbr_delete) { srcentry_t *src; srcentry_t *src_next; mrtentry_t *mrt; mrtentry_t *mrt_srcs; grpentry_t *grp; cand_rp_t *cand_rp; rp_grp_entry_t *rp_grp; rpentry_t *rp; struct uvif *v; IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_INFO, 0, "Deleting PIM neighbor %s", inet_fmt(nbr_delete->address, s1, sizeof(s1))); v = &uvifs[nbr_delete->vifi]; /* Delete the entry from the pim_nbrs chain */ if (nbr_delete->prev) nbr_delete->prev->next = nbr_delete->next; else v->uv_pim_neighbors = nbr_delete->next; if (nbr_delete->next) nbr_delete->next->prev = nbr_delete->prev; return_jp_working_buff(nbr_delete); /* That neighbor could've been the DR */ restart_dr_election(v); /* Update the source entries */ for (src = srclist; src; src = src_next) { src_next = src->next; if (src->upstream != nbr_delete) continue; /* Reset the next hop (PIM) router */ if (set_incoming(src, PIM_IIF_SOURCE) == FALSE) { /* Coudn't reset it. Sorry, the hext hop router toward that * source is probably not a PIM router, or cannot find route * at all, hence I cannot handle this source and have to * delete it. */ logit(LOG_WARNING, 0, "Delete source entry for source %s", inet_fmt(src->address, s1, sizeof(s1))); delete_srcentry(src); } else if (src->upstream) { /* Ignore the local or directly connected sources */ /* Browse all MRT entries for this source and reset the * upstream router. Note that the upstream router is not always * toward the source: it could be toward the RP for example. */ for (mrt = src->mrtlink; mrt; mrt = mrt->srcnext) { if (!(mrt->flags & MRTF_RP)) { mrt->upstream = src->upstream; mrt->metric = src->metric; mrt->preference = src->preference; change_interfaces(mrt, src->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } } } } /* Update the RP entries */ for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) { if (cand_rp->rpentry->upstream != nbr_delete) continue; rp = cand_rp->rpentry; /* Reset the RP entry iif * TODO: check if error setting the iif! */ if (local_address(rp->address) == NO_VIF) { set_incoming(rp, PIM_IIF_RP); } else { rp->incoming = reg_vif_num; rp->upstream = NULL; } mrt = rp->mrtlink; if (mrt) { mrt->upstream = rp->upstream; mrt->metric = rp->metric; mrt->preference = rp->preference; change_interfaces(mrt, rp->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } /* Update the group entries for this RP */ for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp->rpnext) { mrt = grp->grp_route; if (mrt) { mrt->upstream = rp->upstream; mrt->metric = rp->metric; mrt->preference = rp->preference; change_interfaces(mrt, rp->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } /* Update only the (S,G)RPbit entries for this group */ for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) { if (mrt_srcs->flags & MRTF_RP) { mrt_srcs->upstream = rp->upstream; mrt_srcs->metric = rp->metric; mrt_srcs->preference = rp->preference; change_interfaces(mrt_srcs, rp->incoming, mrt_srcs->joined_oifs, mrt_srcs->pruned_oifs, mrt_srcs->leaves, mrt_srcs->asserted_oifs, 0); } } } } } /* Fix GitHub issue #22: Crash in (S,G) state when neighbor is lost */ for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) { for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp->next) { mrt = grp->grp_route; if (mrt && mrt->upstream) { if (mrt->upstream == nbr_delete) mrt->upstream = NULL; } } } } free(nbr_delete); } /* * If all PIM routers on a network segment support DR-Priority we use * that to elect the DR, and use the highest IP address as the tie * breaker. If any routers does *not* support DR-Priority all routers * must use the IP address to elect the DR, this for backwards compat. * * Returns TRUE if we lost the DR role, elected another router. */ static int restart_dr_election(struct uvif *v) { int was_dr = 0, use_dr_prio = 1; uint32_t best_dr_prio = 0; pim_nbr_entry_t *nbr; if (v->uv_flags & VIFF_DR) was_dr = 1; if (!v->uv_pim_neighbors) { /* This was our last neighbor, now we're it. */ IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "All neighbor PIM routers on %s lost, we are the DR now.", inet_fmt(v->uv_lcl_addr, s1, sizeof(s1))); v->uv_flags &= ~VIFF_PIM_NBR; v->uv_flags |= (VIFF_NONBRS | VIFF_DR); return FALSE; } /* Check if all routers on segment advertise DR Priority option * in their PIM Hello messages. Figure out highest prio. */ for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) { if (!nbr->dr_prio_present) { use_dr_prio = 0; break; } if (nbr->dr_prio > best_dr_prio) best_dr_prio = nbr->dr_prio; } /* * RFC4601 sec. 4.3.2 */ if (use_dr_prio) { IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "All routers in %s segment support DR Priority based DR election.", inet_fmt(v->uv_lcl_addr, s1, sizeof(s1))); if (best_dr_prio < v->uv_dr_prio) { v->uv_flags |= VIFF_DR; return FALSE; } if (best_dr_prio == v->uv_dr_prio) goto tiebreak; } else { tiebreak: IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "Using fallback DR election on %s.", inet_fmt(v->uv_lcl_addr, s1, sizeof(s1))); if (ntohl(v->uv_lcl_addr) > ntohl(v->uv_pim_neighbors->address)) { /* The first address is the new potential remote * DR address, but the local address is the winner. */ v->uv_flags |= VIFF_DR; return FALSE; } } if (was_dr) { IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_INFO, 0, "We lost DR role on %s in election.", inet_fmt(v->uv_lcl_addr, s1, sizeof(s1))); v->uv_flags &= ~VIFF_DR; return TRUE; /* Lost election, clean up. */ } return FALSE; } static int validate_pim_opt(uint32_t src, char *str, uint16_t len, uint16_t opt_len) { if (len != opt_len) { IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) logit(LOG_DEBUG, 0, "PIM HELLO %s from %s: invalid OptionLength = %u", str, inet_fmt(src, s1, sizeof(s1)), opt_len); return FALSE; } return TRUE; } static int parse_pim_hello(char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts) { int result = FALSE; size_t rec_len; uint8_t *data; uint16_t opt_type; uint16_t opt_len; /* Assume no opts. */ memset(opts, 0, sizeof(*opts)); /* Body of PIM message */ msg += sizeof(pim_header_t); /* Ignore any data if shorter than (pim_hello header) */ for (len -= sizeof(pim_header_t); len >= sizeof(pim_hello_t); len -= rec_len) { data = (uint8_t *)msg; GET_HOSTSHORT(opt_type, data); GET_HOSTSHORT(opt_len, data); switch (opt_type) { case PIM_HELLO_HOLDTIME: result = validate_pim_opt(src, "Holdtime", PIM_HELLO_HOLDTIME_LEN, opt_len); if (TRUE == result) GET_HOSTSHORT(opts->holdtime, data); break; case PIM_HELLO_DR_PRIO: result = validate_pim_opt(src, "DR Priority", PIM_HELLO_DR_PRIO_LEN, opt_len); if (TRUE == result) { opts->dr_prio_present = 1; GET_HOSTLONG(opts->dr_prio, data); } break; case PIM_HELLO_GENID: result = validate_pim_opt(src, "GenID", PIM_HELLO_GENID_LEN, opt_len); if (TRUE == result) GET_HOSTLONG(opts->genid, data); break; default: break; /* Ignore any unknown options */ } /* Move to the next option */ rec_len = (sizeof(pim_hello_t) + opt_len); if (len < rec_len || result == FALSE) return FALSE; msg += rec_len; } return result; } static void cache_nbr_settings(pim_nbr_entry_t *nbr, pim_hello_opts_t *opts) { SET_TIMER(nbr->timer, opts->holdtime); nbr->genid = opts->genid; nbr->dr_prio = opts->dr_prio; nbr->dr_prio_present = opts->dr_prio_present; } int send_pim_hello(struct uvif *v, uint16_t holdtime) { char *buf; uint8_t *data; size_t len; buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); data = (uint8_t *)buf; PUT_HOSTSHORT(PIM_HELLO_HOLDTIME, data); PUT_HOSTSHORT(PIM_HELLO_HOLDTIME_LEN, data); PUT_HOSTSHORT(holdtime, data); PUT_HOSTSHORT(PIM_HELLO_DR_PRIO, data); PUT_HOSTSHORT(PIM_HELLO_DR_PRIO_LEN, data); PUT_HOSTLONG(v->uv_dr_prio, data); #ifdef ENABLE_PIM_HELLO_GENID PUT_HOSTSHORT(PIM_HELLO_GENID, data); PUT_HOSTSHORT(PIM_HELLO_GENID_LEN, data); PUT_HOSTLONG(v->uv_genid, data); #endif len = data - (uint8_t *)buf; send_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO, len); SET_TIMER(v->uv_hello_timer, pim_timer_hello_interval); return TRUE; } /************************************************************************ * PIM_REGISTER ************************************************************************/ /* TODO: XXX: IF THE BORDER BIT IS SET, THEN * FORWARD THE WHOLE PACKET FROM USER SPACE * AND AT THE SAME TIME IGNORE ANY CACHE_MISS * SIGNALS FROM THE KERNEL. */ int receive_pim_register(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len) { uint32_t inner_src, inner_grp; pim_register_t *reg; struct ip *ip; uint32_t is_border, is_null; mrtentry_t *mrtentry; mrtentry_t *mrtentry2; vifbitmap_t oifs; /* * If instance specific multicast routing table is in use, check * that we are the target of the register packet. Otherwise we * might end up responding to register packet belonging to another * pimd instance. If we are not an RP candidate, we shouldn't have * pimreg interface and shouldn't receive register packets, but we'll * check the cand_rp flag anyway, just to be on the safe side. */ if (mrt_table_id != 0) { if (!cand_rp_flag || my_cand_rp_address != reg_dst) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "PIM register: packet from %s to %s is not destined for us", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2))); return FALSE; } } IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_INFO, 0, "Received PIM register: len = %d from %s", len, inet_fmt(reg_src, s1, sizeof(s1))); /* * Message length validation. * This is suppose to be done in the kernel, but some older kernel * versions do not pefrorm the check for the NULL register messages. */ if (len < sizeof(pim_header_t) + sizeof(pim_register_t) + sizeof(struct ip)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_INFO, 0, "PIM register: short packet (len = %d) from %s", len, inet_fmt(reg_src, s1, sizeof(s1))); return FALSE; } /* * XXX: For PIM_REGISTER the checksum does not include * the inner IP packet. However, some older routers might * create the checksum over the whole packet. Hence, * verify the checksum over the first 8 bytes, and if fails, * then over the whole Register */ if ((inet_cksum((uint16_t *)msg, sizeof(pim_header_t) + sizeof(pim_register_t))) && (inet_cksum((uint16_t *)msg, len))) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "PIM REGISTER from DR %s: invalid PIM header checksum", inet_fmt(reg_src, s1, sizeof(s1))); return FALSE; } /* Lookup register message flags */ reg = (pim_register_t *)(msg + sizeof(pim_header_t)); is_border = ntohl(reg->reg_flags) & PIM_REGISTER_BORDER_BIT; is_null = ntohl(reg->reg_flags) & PIM_REGISTER_NULL_REGISTER_BIT; /* initialize the pointer to the encapsulated packet */ ip = (struct ip *)(msg + sizeof(pim_header_t) + sizeof(pim_register_t)); /* check the IP version (especially for the NULL register...see above) */ if (ip->ip_v != IPVERSION && (! is_null)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_INFO, 0, "PIM register: incorrect IP version (%d) of the inner packet from %s", ip->ip_v, inet_fmt(reg_src, s1, sizeof(s1))); return FALSE; } /* We are keeping all addresses in network order, so no need for ntohl()*/ inner_src = ip->ip_src.s_addr; inner_grp = ip->ip_dst.s_addr; /* * inner_src and inner_grp must be valid IP unicast and multicast address * respectively. XXX: not in the spec. * PIM-SSM support: inner_grp must not be in PIM-SSM range */ if ((!inet_valid_host(inner_src)) || (!IN_MULTICAST(ntohl(inner_grp))) || IN_PIM_SSM_RANGE(inner_grp)) { if (!inet_valid_host(inner_src)) { logit(LOG_WARNING, 0, "Inner source address of register message by %s is invalid: %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2))); } if (!IN_MULTICAST(ntohl(inner_grp))) { logit(LOG_WARNING, 0, "Inner group address of register message by %s is invalid: %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2))); } send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return FALSE; } mrtentry = find_route(inner_src, inner_grp, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (!mrtentry) { /* No routing entry. Send REGISTER_STOP and return. */ IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "No routing entry for source %s and/or group %s" , inet_fmt(inner_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2))); /* TODO: XXX: shouldn't it be inner_src=INADDR_ANY? Not in the spec. */ send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } /* Check if I am the RP for that group */ if ((local_address(reg_dst) == NO_VIF) || !check_mrtentry_rp(mrtentry, reg_dst)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "Not RP in address %s", inet_fmt(reg_dst, s1, sizeof(s1))); send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } /* I am the RP */ if (mrtentry->flags & MRTF_SG) { /* (S,G) found */ /* TODO: check the timer again */ SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT); /* restart timer */ if (!(mrtentry->flags & MRTF_SPT)) { /* The SPT bit is not set */ if (!is_null) { calc_oifs(mrtentry, &oifs); if (VIFM_ISEMPTY(oifs) && (mrtentry->incoming == reg_vif_num)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s", inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2))); send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } /* * TODO: XXX: BUG!!! * The data will be forwarded by the kernel MFC!!! * Need to set a special flag for this routing entry so after * a cache miss occur, the multicast packet will be forwarded * from user space and won't install entry in the kernel MFC. * The problem is that the kernel MFC doesn't know the * PMBR address and simply sets the multicast forwarding * cache to accept/forward all data coming from the * register_vif. */ if (is_border) { if (mrtentry->pmbr_addr != reg_src) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "pmbr_addr (%s) != reg_src (%s)", inet_fmt(mrtentry->pmbr_addr, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2))); send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } } return TRUE; } /* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..?*/ return TRUE; } else { /* The SPT bit is set */ IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "SPT bit is set for group %s source %s", inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2))); send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } } if (mrtentry->flags & (MRTF_WC | MRTF_PMBR)) { if (is_border) { /* Create (S,G) state. The oifs will be the copied from the * existing (*,G) or (*,*,RP) entry. */ mrtentry2 = find_route(inner_src, inner_grp, MRTF_SG, CREATE); if (mrtentry2) { mrtentry2->pmbr_addr = reg_src; /* Clear the SPT flag */ mrtentry2->flags &= ~(MRTF_SPT | MRTF_NEW); SET_TIMER(mrtentry2->timer, PIM_DATA_TIMEOUT); /* TODO: explicitly call the Join/Prune send function? */ FIRE_TIMER(mrtentry2->jp_timer); /* Send the Join immediately */ /* TODO: explicitly call this function? send_pim_join_prune(mrtentry2->upstream->vifi, mrtentry2->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } } } if (mrtentry->flags & MRTF_WC) { /* First PIM Register for this routing entry, log it */ logit(LOG_INFO, 0, "Received PIM REGISTER: src %s, group %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2))); /* (*,G) entry */ calc_oifs(mrtentry, &oifs); if (VIFM_ISEMPTY(oifs)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s (*,G)", inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2))); send_pim_register_stop(reg_dst, reg_src, inner_grp, INADDR_ANY_N); return FALSE; } else { /* XXX: TODO: check with the spec again */ if (!is_null) { uint32_t mfc_source = inner_src; /* Install cache entry in the kernel */ /* TODO: XXX: probably redundant here, because the * decapsulated mcast packet in the kernel will * result in CACHE_MISS */ #ifdef KERNEL_MFC_WC_G if (!(mrtentry->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrtentry, mfc_source, inner_grp, 0); k_chg_mfc(igmp_socket, mfc_source, inner_grp, mrtentry->incoming, mrtentry->oifs, mrtentry->group->rpaddr); return TRUE; } } return TRUE; } if (mrtentry->flags & MRTF_PMBR) { /* (*,*,RP) entry */ if (!is_null) { uint32_t mfc_source = inner_src; /* XXX: have to create either (S,G) or (*,G). * The choice below is (*,G) */ mrtentry2 = find_route(INADDR_ANY_N, inner_grp, MRTF_WC, CREATE); if (!mrtentry2) return FALSE; if (mrtentry2->flags & MRTF_NEW) { /* TODO: something else? Have the feeling sth is missing */ mrtentry2->flags &= ~MRTF_NEW; /* TODO: XXX: copy the timer from the (*,*,RP) entry? */ COPY_TIMER(mrtentry->timer, mrtentry2->timer); } /* Install cache entry in the kernel */ #ifdef KERNEL_MFC_WC_G if (!(mrtentry->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrtentry, mfc_source, inner_grp, 0); k_chg_mfc(igmp_socket, mfc_source, inner_grp, mrtentry->incoming, mrtentry->oifs, mrtentry2->group->rpaddr); return TRUE; } } /* Shoudn't happen: invalid routing entry? */ /* XXX: TODO: shoudn't be inner_src=INADDR_ANY? Not in the spec. */ IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_DEBUG, 0, "Shoudn't happen: invalid routing entry? (%s, %s, %s, %s)", inet_fmt(reg_dst, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2)), inet_fmt(inner_grp, s3, sizeof(s3)), inet_fmt(inner_src, s4, sizeof(s4))); send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } int send_pim_register(char *packet) { struct ip *ip; uint32_t source, group; vifi_t vifi; rpentry_t *rpentry; mrtentry_t *mrtentry; mrtentry_t *mrtentry2; uint32_t reg_src, reg_dst; int reg_mtu, pktlen = 0; char *buf; ip = (struct ip *)packet; source = ip->ip_src.s_addr; group = ip->ip_dst.s_addr; if (IN_PIM_SSM_RANGE(group)) return FALSE; /* Group is in PIM-SSM range, don't send register. */ if ((vifi = find_vif_direct_local(source)) == NO_VIF) return FALSE; if (!(uvifs[vifi].uv_flags & VIFF_DR)) return FALSE; /* I am not the DR for that subnet */ rpentry = rp_match(group); if (!rpentry) return FALSE; /* No RP for this group */ if (local_address(rpentry->address) != NO_VIF) { /* TODO: XXX: not sure it is working! */ return FALSE; /* I am the RP for this group */ } mrtentry = find_route(source, group, MRTF_SG, CREATE); if (!mrtentry) return FALSE; /* Cannot create (S,G) state */ if (mrtentry->flags & MRTF_NEW) { /* A new entry, log it */ reg_src = uvifs[vifi].uv_lcl_addr; reg_dst = mrtentry->group->rpaddr; logit(LOG_INFO, 0, "Send PIM REGISTER: src %s dst %s, group %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)), inet_fmt(group, s3, sizeof(s3))); mrtentry->flags &= ~MRTF_NEW; RESET_TIMER(mrtentry->rs_timer); /* Reset the Register-Suppression timer */ mrtentry2 = mrtentry->group->grp_route; if (!mrtentry2) mrtentry2 = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink; if (mrtentry2) { FIRE_TIMER(mrtentry2->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call this function? send_pim_join_prune(mrtentry2->upstream->vifi, mrtentry2->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } } /* Restart the (S,G) Entry-timer */ SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT); IF_TIMER_NOT_SET(mrtentry->rs_timer) { /* The Register-Suppression Timer is not running. * Encapsulate the data and send to the RP. */ buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); memset(buf, 0, sizeof(pim_register_t)); /* No flags set */ buf += sizeof(pim_register_t); /* Copy the data packet at the back of the register packet */ pktlen = ntohs(ip->ip_len); memcpy(buf, ip, pktlen); pktlen += sizeof(pim_register_t); /* 'sizeof(struct ip) + sizeof(pim_header_t)' added by send_pim() */ reg_mtu = uvifs[vifi].uv_mtu; /* XXX: Use PMTU to RP instead! */ reg_src = uvifs[vifi].uv_lcl_addr; reg_dst = mrtentry->group->rpaddr; send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen); return TRUE; } return TRUE; } int send_pim_null_register(mrtentry_t *mrtentry) { struct ip *ip; pim_register_t *pim_register; int reg_mtu, pktlen; vifi_t vifi; uint32_t reg_src, reg_dst; /* No directly connected source; no local address */ if ((vifi = find_vif_direct_local(mrtentry->source->address))== NO_VIF) return FALSE; pim_register = (pim_register_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); memset(pim_register, 0, sizeof(pim_register_t)); pim_register->reg_flags = htonl(pim_register->reg_flags | PIM_REGISTER_NULL_REGISTER_BIT); ip = (struct ip *)(pim_register + 1); /* set src/dst in dummy hdr */ ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) >> 2); ip->ip_tos = 0; ip->ip_id = 0; ip->ip_off = 0; ip->ip_p = IPPROTO_UDP; /* XXX: bogus */ ip->ip_len = htons(sizeof(struct ip)); ip->ip_ttl = MINTTL; /* TODO: XXX: check whether need to setup the ttl */ ip->ip_src.s_addr = mrtentry->source->address; ip->ip_dst.s_addr = mrtentry->group->group; ip->ip_sum = 0; ip->ip_sum = inet_cksum((uint16_t *)ip, sizeof(struct ip)); /* include the dummy ip header */ pktlen = sizeof(pim_register_t) + sizeof(struct ip); reg_mtu = uvifs[vifi].uv_mtu; reg_dst = mrtentry->group->rpaddr; reg_src = uvifs[vifi].uv_lcl_addr; send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen); return TRUE; } /************************************************************************ * PIM_REGISTER_STOP ************************************************************************/ int receive_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len) { pim_encod_grp_addr_t egaddr; pim_encod_uni_addr_t eusaddr; uint8_t *data; mrtentry_t *mrtentry; vifbitmap_t pruned_oifs; /* Checksum */ if (inet_cksum((uint16_t *)msg, len)) return FALSE; data = (uint8_t *)(msg + sizeof(pim_header_t)); GET_EGADDR(&egaddr, data); GET_EUADDR(&eusaddr, data); logit(LOG_INFO, 0, "Received PIM_REGISTER_STOP from RP %s to %s for src = %s and group = %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)), inet_fmt(eusaddr.unicast_addr, s3, sizeof(s3)), inet_fmt(egaddr.mcast_addr, s4, sizeof(s4))); /* TODO: apply the group mask and do register_stop for all grp addresses */ /* TODO: check for SourceAddress == 0 */ mrtentry = find_route(eusaddr.unicast_addr, egaddr.mcast_addr, MRTF_SG, DONT_CREATE); if (!mrtentry) return FALSE; /* XXX: not in the spec: check if the PIM_REGISTER_STOP originator is * really the RP */ if (check_mrtentry_rp(mrtentry, reg_src) == FALSE) return FALSE; /* restart the Register-Suppression timer */ SET_TIMER(mrtentry->rs_timer, (0.5 * PIM_REGISTER_SUPPRESSION_TIMEOUT) + (RANDOM() % (PIM_REGISTER_SUPPRESSION_TIMEOUT + 1))); /* Prune the register_vif from the outgoing list */ VIFM_COPY(mrtentry->pruned_oifs, pruned_oifs); VIFM_SET(reg_vif_num, pruned_oifs); change_interfaces(mrtentry, mrtentry->incoming, mrtentry->joined_oifs, pruned_oifs, mrtentry->leaves, mrtentry->asserted_oifs, 0); return TRUE; } /* TODO: optional rate limiting is not implemented yet */ /* Unicasts a REGISTER_STOP message to the DR */ static int send_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_src) { char *buf; uint8_t *data; if (IN_PIM_SSM_RANGE(inner_grp)) return TRUE; logit(LOG_INFO, 0, "Send PIM REGISTER STOP from %s to router %s for src = %s and group = %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)), inet_fmt(inner_src, s3, sizeof(s3)), inet_fmt(inner_grp, s4, sizeof(s4))); buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); data = (uint8_t *)buf; PUT_EGADDR(inner_grp, SINGLE_GRP_MSKLEN, 0, data); PUT_EUADDR(inner_src, data); send_pim_unicast(pim_send_buf, 0, reg_src, reg_dst, PIM_REGISTER_STOP, data - (uint8_t *)buf); return TRUE; } /************************************************************************ * PIM_JOIN_PRUNE ************************************************************************/ int join_or_prune(mrtentry_t *mrtentry, pim_nbr_entry_t *upstream_router) { vifbitmap_t entry_oifs; mrtentry_t *mrtentry_grp; if (!mrtentry || !upstream_router) return PIM_ACTION_NOTHING; calc_oifs(mrtentry, &entry_oifs); if (mrtentry->flags & (MRTF_PMBR | MRTF_WC)) { if (IN_PIM_SSM_RANGE(mrtentry->group->group)) { logit(LOG_DEBUG, 0, "No action for SSM (PMBR|WC)"); return PIM_ACTION_NOTHING; } /* (*,*,RP) or (*,G) entry */ /* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */ if (upstream_router != mrtentry->upstream) return PIM_ACTION_NOTHING; /* TODO: XXX: Can we have (*,*,RP) prune? */ if (VIFM_ISEMPTY(entry_oifs)) { /* NULL oifs */ if (!(uvifs[mrtentry->incoming].uv_flags & VIFF_DR)) /* I am not the DR for that subnet. */ return PIM_ACTION_PRUNE; if (VIFM_ISSET(mrtentry->incoming, mrtentry->leaves)) /* I am the DR and have local leaves */ return PIM_ACTION_JOIN; /* Probably the last local member hast timeout */ return PIM_ACTION_PRUNE; } return PIM_ACTION_JOIN; } if (mrtentry->flags & MRTF_SG) { /* (S,G) entry */ /* TODO: check again */ if (mrtentry->upstream == upstream_router) { if (!(mrtentry->flags & MRTF_RP)) { /* Upstream router toward S */ if (VIFM_ISEMPTY(entry_oifs)) { if (mrtentry->group->active_rp_grp && mrtentry->group->rpaddr == my_cand_rp_address) { /* (S,G) at the RP. Don't send Join/Prune * (see the end of Section 3.3.2) */ return PIM_ACTION_NOTHING; } return PIM_ACTION_PRUNE; } else { return PIM_ACTION_JOIN; } } else { if (IN_PIM_SSM_RANGE(mrtentry->group->group)) { logit(LOG_DEBUG, 0, "No action for SSM (RP)"); return PIM_ACTION_NOTHING; } /* Upstream router toward RP */ if (VIFM_ISEMPTY(entry_oifs)) return PIM_ACTION_PRUNE; } } /* Looks like the case when the upstream router toward S is * different from the upstream router toward RP */ if (!mrtentry->group->active_rp_grp) return PIM_ACTION_NOTHING; mrtentry_grp = mrtentry->group->grp_route; if (!mrtentry_grp) { mrtentry_grp = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink; if (!mrtentry_grp) return PIM_ACTION_NOTHING; } if (mrtentry_grp->upstream != upstream_router) return PIM_ACTION_NOTHING; /* XXX: shoudn't happen */ if (!(mrtentry->flags & MRTF_RP) && (mrtentry->flags & MRTF_SPT)) return PIM_ACTION_PRUNE; } return PIM_ACTION_NOTHING; } /* * Log PIM Join/Prune message. Send log event for every join and prune separately. * Format of the Join/Prune message starting from dataptr is following: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | Multicast Group Address 1 (Encoded-Group format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Number of Joined Sources | Number of Pruned Sources | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Joined Source Address 1 (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | . | | . | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Joined Source Address n (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pruned Source Address 1 (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | . | | . | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pruned Source Address n (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Multicast Group Address m (Encoded-Group format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Number of Joined Sources | Number of Pruned Sources | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Joined Source Address 1 (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | . | | . | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Joined Source Address n (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pruned Source Address 1 (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | . | | . | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pruned Source Address n (Encoded-Source format) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ void log_pim_join_prune(uint32_t src, uint8_t *data_ptr, int num_groups, char* ifname) { pim_encod_grp_addr_t encod_group; pim_encod_src_addr_t encod_src; uint32_t group, source; uint16_t num_j_srcs; uint16_t num_p_srcs; /* Message validity check is done by caller */ while (num_groups--) { GET_EGADDR(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); group = encod_group.mcast_addr; while(num_j_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; logit(LOG_INFO, 0, "Received PIM JOIN from %s to group %s for multicast source %s on %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname); } while (num_p_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; logit(LOG_INFO, 0, "Received PIM PRUNE from %s to group %s for multicast source %s on %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname); } } } /* TODO: when parsing, check if we go beyond message size */ /* TODO: too long, simplify it! */ #define PIM_JOIN_PRUNE_MINLEN (4 + PIM_ENCODE_UNI_ADDR_LEN + 4) int receive_pim_join_prune(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len) { vifi_t vifi; struct uvif *v; pim_encod_uni_addr_t eutaddr; pim_encod_grp_addr_t egaddr; pim_encod_src_addr_t esaddr; uint8_t *data; uint8_t *data_start; uint8_t *data_group_end; uint8_t num_groups; uint8_t num_groups_tmp; int star_star_rp_found; uint16_t holdtime; uint16_t num_j_srcs; uint16_t num_j_srcs_tmp; uint16_t num_p_srcs; uint32_t source; uint32_t group; uint32_t s_mask; uint32_t g_mask; uint8_t s_flags; uint8_t reserved __attribute__((unused)); rpentry_t *rpentry; mrtentry_t *mrt; mrtentry_t *mrt_srcs; mrtentry_t *mrt_rp; grpentry_t *grp; uint16_t jp_value; pim_nbr_entry_t *upstream_router; int my_action; int ignore_group; rp_grp_entry_t *rp_grp; uint8_t *data_group_j_start; uint8_t *data_group_p_start; if ((vifi = find_vif_direct(src)) == NO_VIF) { /* Either a local vif or somehow received PIM_JOIN_PRUNE from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) { logit(LOG_DEBUG, 0, "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); } return FALSE; } /* Checksum */ if (inet_cksum((uint16_t *)msg, len)) return FALSE; v = &uvifs[vifi]; if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ /* sanity check for the minimum length */ if (len < PIM_JOIN_PRUNE_MINLEN) { logit(LOG_NOTICE, 0, "%s(): Too short Join/Prune message (%u bytes) from %s on %s", __func__, len, inet_fmt(src, s1, sizeof(s1)), v->uv_name); return FALSE; } len -= PIM_JOIN_PRUNE_MINLEN; data = (uint8_t *)(msg + sizeof(pim_header_t)); /* Get the target address */ GET_EUADDR(&eutaddr, data); GET_BYTE(reserved, data); GET_BYTE(num_groups, data); GET_HOSTSHORT(holdtime, data); if (num_groups == 0) { /* No indication for groups in the message */ logit(LOG_NOTICE, 0, "%s(): No groups in Join/Prune message from %s on %s!", __func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name); return FALSE; } logit(LOG_INFO, 0, "Received PIM JOIN/PRUNE from %s on %s", inet_fmt(src, s1, sizeof(s1)), v->uv_name); /* Sanity check for the message length through all the groups */ num_groups_tmp = num_groups; data_start = data; while (num_groups_tmp--) { size_t srclen; /* group addr + #join + #src */ if (len < PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t)) { logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is" " too short to contain enough data", __func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name); return FALSE; } len -= (PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t)); data += PIM_ENCODE_GRP_ADDR_LEN; /* joined source addresses and pruned source addresses */ GET_HOSTSHORT(num_j_srcs, data); GET_HOSTSHORT(num_p_srcs, data); srclen = (num_j_srcs + num_p_srcs) * PIM_ENCODE_SRC_ADDR_LEN; if (len < srclen) { logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is" " too short to contain enough data", __func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name); return FALSE; } len -= srclen; data += srclen; } data = data_start; num_groups_tmp = num_groups; /* Sanity check is done. Log the message */ log_pim_join_prune(src, data, num_groups, v->uv_name); if (eutaddr.unicast_addr != v->uv_lcl_addr) { /* if I am not the target of the join message */ /* Join/Prune suppression code. This either modifies the J/P timers * or triggers an overriding Join. */ /* Note that if we have (S,G) prune and (*,G) Join, we must send * them in the same message. We don't bother to modify both timers * here. The Join/Prune sending function will take care of that. */ upstream_router = find_pim_nbr(eutaddr.unicast_addr); if (!upstream_router) return FALSE; /* I have no such neighbor */ while (num_groups--) { GET_EGADDR(&egaddr, data); GET_HOSTSHORT(num_j_srcs, data); GET_HOSTSHORT(num_p_srcs, data); MASKLEN_TO_MASK(egaddr.masklen, g_mask); group = egaddr.mcast_addr; if (!IN_MULTICAST(ntohl(group))) { data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; /* Ignore this group and jump to the next */ } if ((ntohl(group) == CLASSD_PREFIX) && (egaddr.masklen == STAR_STAR_RP_MSKLEN)) { /* (*,*,RP) Join suppression */ while (num_j_srcs--) { GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* This is the RP address. */ rpentry = rp_find(source); if (!rpentry) continue; /* Don't have such RP. Ignore */ mrt_rp = rpentry->mrtlink; my_action = join_or_prune(mrt_rp, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrt_rp->jp_timer > holdtime) continue; if ((mrt_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; /* Set the Join/Prune suppression timer for this * routing entry by increasing the current * Join/Prune timer. */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); /* TODO: XXX: TIMER implem. dependency! */ if (mrt_rp->jp_timer < jp_value) SET_TIMER(mrt_rp->jp_timer, jp_value); } } /* num_j_srcs */ while (num_p_srcs--) { /* TODO: XXX: Can we have (*,*,RP) prune message? * Not in the spec, but anyway, the code below * can handle them: either suppress * the local (*,*,RP) prunes or override the prunes by * sending (*,*,RP) and/or (*,G) and/or (S,G) Join. */ GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* This is the RP address. */ rpentry = rp_find(source); if (!rpentry) continue; /* Don't have such RP. Ignore */ mrt_rp = rpentry->mrtlink; my_action = join_or_prune(mrt_rp, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* TODO: XXX: TIMER implem. dependency! */ if ((mrt_rp->jp_timer < holdtime) || ((mrt_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { /* Suppress the Prune */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrt_rp->jp_timer < jp_value) SET_TIMER(mrt_rp->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrt_rp->jp_timer > jp_value) SET_TIMER(mrt_rp->jp_timer, jp_value); } /* Check all (*,G) and (S,G) matching to this RP. * If my_action == JOIN, then send a Join and override * the (*,*,RP) Prune. */ for (grp = rpentry->cand_rp->rp_grp_next->grplink; grp; grp = grp->rpnext) { my_action = join_or_prune(grp->grp_route, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (grp->grp_route->jp_timer > jp_value) SET_TIMER(grp->grp_route->jp_timer, jp_value); } for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) { my_action = join_or_prune(mrt_srcs, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrt_srcs->jp_timer > jp_value) SET_TIMER(mrt_srcs->jp_timer, jp_value); } } /* For all (S,G) */ } /* For all (*,G) */ } } /* num_p_srcs */ continue; /* This was (*,*,RP) suppression */ } /* (*,G) or (S,G) suppression */ /* TODO: XXX: currently, accumulated groups * (i.e. group_masklen < egaddress_lengt) are not * implemented. Just need to create a loop and apply the * procedure below for all groups matching the prefix. */ while (num_j_srcs--) { GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) JOIN_REQUEST (toward the RP) */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrt) continue; my_action = join_or_prune(mrt, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* (*,G) Join suppresion */ if (source != mrt->group->active_rp_grp->rp->rpentry->address) continue; /* The RP address doesn't match. Ignore. */ /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrt->jp_timer > holdtime) continue; if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrt->jp_timer < jp_value) SET_TIMER(mrt->jp_timer, jp_value); continue; } /* End of (*,G) Join suppression */ /* (S,G) Join suppresion */ mrt = find_route(source, group, MRTF_SG, DONT_CREATE); if (!mrt) continue; my_action = join_or_prune(mrt, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrt->jp_timer > holdtime) continue; if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrt->jp_timer < jp_value) SET_TIMER(mrt->jp_timer, jp_value); continue; } /* Prunes suppression */ while (num_p_srcs--) { GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) prune suppression */ rpentry = rp_match(group); if (!rpentry || (rpentry->address != source)) continue; /* No such RP or it is different. Ignore */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrt) continue; my_action = join_or_prune(mrt, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* TODO: XXX: TIMER implem. dependency! */ if ((mrt->jp_timer < holdtime) || ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { /* Suppress the Prune */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrt->jp_timer < jp_value) SET_TIMER(mrt->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrt->jp_timer > jp_value) SET_TIMER(mrt->jp_timer, jp_value); } /* Check all (S,G) entries for this group. * If my_action == JOIN, then send the Join and override * the (*,G) Prune. */ for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) { my_action = join_or_prune(mrt_srcs, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrt->jp_timer > jp_value) SET_TIMER(mrt->jp_timer, jp_value); } } /* For all (S,G) */ continue; /* End of (*,G) prune suppression */ } /* (S,G) prune suppression */ mrt = find_route(source, group, MRTF_SG, DONT_CREATE); if (!mrt) continue; my_action = join_or_prune(mrt, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* Suppress the (S,G) Prune */ /* TODO: XXX: TIMER implem. dependency! */ if ((mrt->jp_timer < holdtime) || ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrt->jp_timer < jp_value) SET_TIMER(mrt->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrt->jp_timer > jp_value) SET_TIMER(mrt->jp_timer, jp_value); } } /* while (num_p_srcs--) */ } /* while (num_groups--) */ return TRUE; } /* End of Join/Prune suppression code */ /* I am the target of this join, so process the message */ /* The spec says that if there is (*,G) Join, it has priority over * old existing ~(S,G) prunes in the routing table. * However, if the (*,G) Join and the ~(S,G) prune are in * the same message, ~(S,G) has the priority. The spec doesn't say it, * but I think the same is true for (*,*,RP) and ~(S,G) prunes. * * The code below do: * (1) Check the whole message for (*,*,RP) Joins. * (1.1) If found, clean all pruned_oifs for all (*,G) and all (S,G) * for each RP in the list, but do not update the kernel cache. * Then go back to the beginning of the message and start * processing for each group: * (2) Check for Prunes. If no prunes, process the Joins. * (3) If there are Prunes: * (3.1) Scan the Join part for existing (*,G) Join. * (3.1.1) If there is (*,G) Join, clear join interface from * the pruned_oifs for all (S,G), but DO NOT flush the * change to the kernel (by using change_interfaces() * for example) * (3.2) After the pruned_oifs are eventually cleared in (3.1.1), * process the Prune part of the message normally * (setting the prune_oifs and flashing the changes to the (kernel). * (3.3) After the Prune part is processed, process the Join part * normally (by applying any changes to the kernel) * (4) If there were (*,*,RP) Join/Prune, process them. * * If the Join/Prune list is too long, it may result in long processing * overhead. The idea above is not to place any wrong info in the * kernel, because it may result in short-time existing traffic * forwarding on wrong interface. * Hopefully, in the future will find a better way to implement it. */ num_groups_tmp = num_groups; data_start = data; star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */ while (num_groups_tmp--) { /* Search for (*,*,RP) Join */ GET_EGADDR(&egaddr, data); GET_HOSTSHORT(num_j_srcs, data); GET_HOSTSHORT(num_p_srcs, data); group = egaddr.mcast_addr; if ((ntohl(group) != CLASSD_PREFIX) || (egaddr.masklen != STAR_STAR_RP_MSKLEN)) { /* This is not (*,*,RP). Jump to the next group. */ data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } /* (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear * the pruned oif, but do not update the kernel. */ star_star_rp_found = TRUE; while (num_j_srcs--) { GET_ESADDR(&esaddr, data); rpentry = rp_find(esaddr.src_addr); if (!rpentry) continue; for (rp_grp = rpentry->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp->rpnext) { if (grp->grp_route) VIFM_CLR(vifi, grp->grp_route->pruned_oifs); for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext) VIFM_CLR(vifi, mrt->pruned_oifs); } } } data += (num_p_srcs) * sizeof(pim_encod_src_addr_t); } /* * Start processing the groups. If this is (*,*,RP), skip it, but process * it at the end. */ data = data_start; num_groups_tmp = num_groups; while (num_groups_tmp--) { GET_EGADDR(&egaddr, data); GET_HOSTSHORT(num_j_srcs, data); GET_HOSTSHORT(num_p_srcs, data); group = egaddr.mcast_addr; if (!IN_MULTICAST(ntohl(group))) { data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; /* Ignore this group and jump to the next one */ } if ((ntohl(group) == CLASSD_PREFIX) && (egaddr.masklen == STAR_STAR_RP_MSKLEN)) { /* This is (*,*,RP). Jump to the next group. */ data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } rpentry = rp_match(group); if (!rpentry) { data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } data_group_j_start = data; data_group_p_start = data + num_j_srcs * sizeof(pim_encod_src_addr_t); data_group_end = data + (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); /* Scan the Join part for (*,G) Join and then clear the * particular interface from pruned_oifs for all (S,G). * If the RP address in the Join message is different from * the local match, ignore the whole group. */ num_j_srcs_tmp = num_j_srcs; ignore_group = FALSE; while (num_j_srcs_tmp--) { GET_ESADDR(&esaddr, data); if ((esaddr.flags & USADDR_RP_BIT) && (esaddr.flags & USADDR_WC_BIT)) { /* This is the RP address, i.e. (*,G) Join. * Check if the RP-mapping is consistent and if "yes", * then Reset the pruned_oifs for all (S,G) entries. */ if (rpentry->address != esaddr.src_addr) { ignore_group = TRUE; break; } mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (mrt) { for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) VIFM_CLR(vifi, mrt_srcs->pruned_oifs); } break; } } if (ignore_group == TRUE) { data += (num_j_srcs_tmp + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } data = data_group_p_start; /* Process the Prune part first */ while (num_p_srcs--) { GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) { /* (S,G) prune sent toward S */ mrt = find_route(source, group, MRTF_SG, DONT_CREATE); if (!mrt) continue; /* I don't have (S,G) to prune. Ignore. */ /* If the link is point-to-point, timeout the oif * immediately, otherwise decrease the timer to allow * other downstream routers to override the prune. */ /* TODO: XXX: increase the entry timer? */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrt->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi]) SET_TIMER(mrt->vif_timers[vifi], mrt->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) { VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } continue; } if ((s_flags & USADDR_RP_BIT) && (!(s_flags & USADDR_WC_BIT))) { /* ~(S,G)RPbit prune sent toward the RP */ mrt = find_route(source, group, MRTF_SG, DONT_CREATE); if (mrt) { SET_TIMER(mrt->timer, holdtime); if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrt->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi]) SET_TIMER(mrt->vif_timers[vifi], mrt->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) { VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } continue; } /* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrt) { mrt = find_route(source, group, MRTF_SG | MRTF_RP, CREATE); if (!mrt) continue; mrt->flags &= ~MRTF_NEW; RESET_TIMER(mrt->vif_timers[vifi]); /* TODO: XXX: The spec doens't say what value to use for * the entry time. Use the J/P holdtime. */ SET_TIMER(mrt->timer, holdtime); /* TODO: XXX: The spec says to delete the oif. However, * its timer only should be lowered, so the prune can be * overwritten on multiaccess LAN. Spec BUG. */ VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } continue; } if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) Prune */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrt) { if (mrt->flags & MRTF_WC) { /* TODO: XXX: Should check the whole Prune list in * advance for (*,G) prune and if the RP address * does not match the local RP-map, then ignore the * whole group, not only this particular (*,G) prune. */ if (mrt->group->active_rp_grp->rp->rpentry->address != source) continue; /* The RP address doesn't match. */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrt->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi]) SET_TIMER(mrt->vif_timers[vifi], mrt->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) { VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } continue; } /* No (*,G) entry, but found (*,*,RP). Create (*,G) */ if (mrt->source->address != source) continue; /* The RP address doesn't match. */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); if (!mrt) continue; mrt->flags &= ~MRTF_NEW; RESET_TIMER(mrt->vif_timers[vifi]); /* TODO: XXX: should only lower the oif timer, so it can * be overwritten on multiaccess LAN. Spec bug. */ VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } /* (*,G) or (*,*,RP) found */ } /* (*,G) prune */ } /* while (num_p_srcs--) */ /* End of (S,G) and (*,G) Prune handling */ /* Jump back to the Join part and process it */ data = data_group_j_start; while (num_j_srcs--) { GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); if ((s_flags & USADDR_WC_BIT) && (s_flags & USADDR_RP_BIT)) { /* (*,G) Join toward RP */ /* It has been checked already that this RP address is * the same as the local RP-maping. */ mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); if (!mrt) continue; VIFM_SET(vifi, mrt->joined_oifs); VIFM_CLR(vifi, mrt->pruned_oifs); VIFM_CLR(vifi, mrt->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] < holdtime) { SET_TIMER(mrt->vif_timers[vifi], holdtime); mrt->vif_deletion_delay[vifi] = holdtime/3; } if (mrt->timer < holdtime) SET_TIMER(mrt->timer, holdtime); mrt->flags &= ~MRTF_NEW; change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); /* Need to update the (S,G) entries, because of the previous * cleaning of the pruned_oifs. The reason is that if the * oifs for (*,G) weren't changed, the (S,G) entries won't * be updated by change_interfaces() */ for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) change_interfaces(mrt_srcs, mrt_srcs->incoming, mrt_srcs->joined_oifs, mrt_srcs->pruned_oifs, mrt_srcs->leaves, mrt_srcs->asserted_oifs, 0); continue; } if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) { /* (S,G) Join toward S */ if (vifi == get_iif(source)) continue; /* Ignore this (S,G) Join */ mrt = find_route(source, group, MRTF_SG, CREATE); if (!mrt) continue; VIFM_SET(vifi, mrt->joined_oifs); VIFM_CLR(vifi, mrt->pruned_oifs); VIFM_CLR(vifi, mrt->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] < holdtime) { SET_TIMER(mrt->vif_timers[vifi], holdtime); mrt->vif_deletion_delay[vifi] = holdtime/3; } if (mrt->timer < holdtime) SET_TIMER(mrt->timer, holdtime); /* TODO: if this is a new entry, send immediately the * Join message toward S. The Join/Prune timer for new * entries is 0, but it does not means the message will * be sent immediately. */ mrt->flags &= ~MRTF_NEW; /* Note that we must create (S,G) without the RPbit set. * If we already had such entry, change_interfaces() will * reset the RPbit propertly. */ change_interfaces(mrt, mrt->source->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); continue; } } /* while (num_j_srcs--) */ data = data_group_end; } /* for all groups */ /* Now process the (*,*,RP) Join/Prune */ if (star_star_rp_found != TRUE) return TRUE; data = data_start; while (num_groups--) { /* The conservative approach is to scan again the whole message, * just in case if we have more than one (*,*,RP) requests. */ GET_EGADDR(&egaddr, data); GET_HOSTSHORT(num_j_srcs, data); GET_HOSTSHORT(num_p_srcs, data); group = egaddr.mcast_addr; if ((ntohl(group) != CLASSD_PREFIX) || (egaddr.masklen != STAR_STAR_RP_MSKLEN)) { /* This is not (*,*,RP). Jump to the next group. */ data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } /* (*,*,RP) found */ while (num_j_srcs--) { /* TODO: XXX: check that the iif is different from the Join oifs */ GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, CREATE); if (!mrt) continue; VIFM_SET(vifi, mrt->joined_oifs); VIFM_CLR(vifi, mrt->pruned_oifs); VIFM_CLR(vifi, mrt->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] < holdtime) { SET_TIMER(mrt->vif_timers[vifi], holdtime); mrt->vif_deletion_delay[vifi] = holdtime/3; } if (mrt->timer < holdtime) SET_TIMER(mrt->timer, holdtime); mrt->flags &= ~MRTF_NEW; change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); /* Need to update the (S,G) and (*,G) entries, because of * the previous cleaning of the pruned_oifs. The reason is * that if the oifs for (*,*,RP) weren't changed, the * (*,G) and (S,G) entries won't be updated by change_interfaces() */ for (rp_grp = mrt->source->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp->rpnext) { /* Update the (*,G) entry */ if (grp->grp_route) { change_interfaces(grp->grp_route, grp->grp_route->incoming, grp->grp_route->joined_oifs, grp->grp_route->pruned_oifs, grp->grp_route->leaves, grp->grp_route->asserted_oifs, 0); } /* Update the (S,G) entries */ for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) change_interfaces(mrt_srcs, mrt_srcs->incoming, mrt_srcs->joined_oifs, mrt_srcs->pruned_oifs, mrt_srcs->leaves, mrt_srcs->asserted_oifs, 0); } } continue; } while (num_p_srcs--) { /* TODO: XXX: can we have (*,*,RP) Prune? */ GET_ESADDR(&esaddr, data); source = esaddr.src_addr; if (!inet_valid_host(source)) continue; s_flags = esaddr.flags; MASKLEN_TO_MASK(esaddr.masklen, s_mask); mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, DONT_CREATE); if (!mrt) continue; /* If the link is point-to-point, timeout the oif * immediately, otherwise decrease the timer to allow * other downstream routers to override the prune. */ /* TODO: XXX: increase the entry timer? */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrt->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi]) SET_TIMER(mrt->vif_timers[vifi], mrt->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) { VIFM_CLR(vifi, mrt->joined_oifs); VIFM_SET(vifi, mrt->pruned_oifs); VIFM_SET(vifi, mrt->asserted_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } } } /* For all groups processing (*,*,R) */ return TRUE; } /* * TODO: NOT USED, probably buggy, but may need it in the future. */ /* * TODO: create two functions: periodic which timeout the timers * and non-periodic which only check but don't timeout the timers. */ /* * Create and send Join/Prune messages per interface. * Only the entries which have the Join/Prune timer expired are included. * In the special case when we have ~(S,G)RPbit Prune entry, we must * include any (*,G) or (*,*,RP) * Currently the whole table is scanned. In the future will have all * routing entries linked in a chain with the corresponding upstream * pim_nbr_entry. * * If pim_nbr is not NULL, then send to only this particular PIM neighbor, */ int send_periodic_pim_join_prune(vifi_t vifi, pim_nbr_entry_t *pim_nbr, uint16_t holdtime) { grpentry_t *grp; mrtentry_t *mrt; rpentry_t *rp; uint32_t addr; struct uvif *v; pim_nbr_entry_t *nbr; cand_rp_t *cand_rp; /* Walk through all routing entries. The iif must match to include the * entry. Check first the (*,G) entry and then all associated (S,G). * At the end of the message will add any (*,*,RP) entries. * TODO: check other PIM-SM implementations and decide the more * appropriate place to put the (*,*,RP) entries: in the beginning of the * message or at the end. */ v = &uvifs[vifi]; /* Check the (*,G) and (S,G) entries */ for (grp = grplist; grp; grp = grp->next) { mrt = grp->grp_route; /* TODO: XXX: TIMER implem. dependency! */ if (mrt && (mrt->incoming == vifi) && (mrt->jp_timer <= TIMER_INTERVAL)) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && mrt->upstream != pim_nbr) continue; /* Don't send (*,G) or (S,G,rpt) Join/Prune */ /* TODO: this handles (S,G,rpt) Join/Prune? */ if (!(mrt->flags & MRTF_SG) && IN_PIM_SSM_RANGE(grp->group)) { logit(LOG_DEBUG, 0, "Skip j/p for SSM (!SG)"); continue; } /* TODO: XXX: The J/P suppression timer is not in the spec! */ if (!VIFM_ISEMPTY(mrt->joined_oifs) || (v->uv_flags & VIFF_DR)) { add_jp_entry(mrt->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, grp->rpaddr, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN); } /* TODO: XXX: TIMER implem. dependency! */ if (VIFM_ISEMPTY(mrt->joined_oifs) && (!(v->uv_flags & VIFF_DR)) && (mrt->jp_timer <= TIMER_INTERVAL)) { add_jp_entry(mrt->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, grp->rpaddr, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE); } } /* Check the (S,G) entries */ for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && mrt->upstream != pim_nbr) continue; if (mrt->flags & MRTF_RP) { /* RPbit set */ addr = mrt->source->address; if (VIFM_ISEMPTY(mrt->joined_oifs) || find_vif_direct_local(addr) != NO_VIF) { /* TODO: XXX: TIMER implem. dependency! */ if (grp->grp_route && grp->grp_route->incoming == vifi && grp->grp_route->jp_timer <= TIMER_INTERVAL) /* S is directly connected. Send toward RP */ add_jp_entry(grp->grp_route->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, addr, SINGLE_SRC_MSKLEN, MRTF_RP, PIM_ACTION_PRUNE); } } else { /* RPbit cleared */ if (VIFM_ISEMPTY(mrt->joined_oifs)) { /* TODO: XXX: TIMER implem. dependency! */ if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL) add_jp_entry(mrt->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, mrt->source->address, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE); } else { logit(LOG_DEBUG, 0 , "Joined not empty, group %s", inet_ntoa(*(struct in_addr *)&grp->group)); /* TODO: XXX: TIMER implem. dependency! */ if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL) add_jp_entry(mrt->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, mrt->source->address, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN); } /* TODO: XXX: TIMER implem. dependency! */ if ((mrt->flags & MRTF_SPT) && grp->grp_route && mrt->incoming != grp->grp_route->incoming && grp->grp_route->incoming == vifi && grp->grp_route->jp_timer <= TIMER_INTERVAL) add_jp_entry(grp->grp_route->upstream, holdtime, grp->group, SINGLE_GRP_MSKLEN, mrt->source->address, SINGLE_SRC_MSKLEN, MRTF_RP, PIM_ACTION_PRUNE); } } } /* Check the (*,*,RP) entries */ for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) { rp = cand_rp->rpentry; /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && rp->upstream != pim_nbr) continue; /* TODO: XXX: TIMER implem. dependency! */ if (rp->mrtlink && rp->incoming == vifi && rp->mrtlink->jp_timer <= TIMER_INTERVAL) { add_jp_entry(rp->upstream, holdtime, htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, rp->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, PIM_ACTION_JOIN); } } /* Send all pending Join/Prune messages */ for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && (nbr != pim_nbr)) continue; pack_and_send_jp_message(nbr); } return TRUE; } int add_jp_entry(pim_nbr_entry_t *pim_nbr, uint16_t holdtime, uint32_t group, uint8_t grp_msklen, uint32_t source, uint8_t src_msklen, uint16_t addr_flags, uint8_t join_prune) { build_jp_message_t *bjpm; uint8_t *data; uint8_t flags = 0; int rp_flag; bjpm = pim_nbr->build_jp_message; if (bjpm) { if ((bjpm->jp_message_size + bjpm->join_list_size + bjpm->prune_list_size + bjpm->rp_list_join_size + bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE) || (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE) || (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE) || (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE) || (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE)) { /* TODO: XXX: BUG: If the list is getting too large, must * be careful with the fragmentation. */ pack_and_send_jp_message(pim_nbr); bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */ } } if (bjpm) { if ((bjpm->curr_group != group) || (bjpm->curr_group_msklen != grp_msklen) || (bjpm->holdtime != holdtime)) { pack_jp_message(pim_nbr); } } if (!bjpm) { bjpm = get_jp_working_buff(); if (!bjpm) { logit(LOG_ERR, 0, "Failed allocating working buffer in add_jp_entry()"); exit (-1); } pim_nbr->build_jp_message = bjpm; data = bjpm->jp_message; PUT_EUADDR(pim_nbr->address, data); PUT_BYTE(0, data); /* Reserved */ bjpm->num_groups_ptr = data++; /* The pointer for numgroups */ *(bjpm->num_groups_ptr) = 0; /* Zero groups */ PUT_HOSTSHORT(holdtime, data); bjpm->holdtime = holdtime; bjpm->jp_message_size = data - bjpm->jp_message; } /* TODO: move somewhere else, only when it is a new group */ bjpm->curr_group = group; bjpm->curr_group_msklen = grp_msklen; if (group == htonl(CLASSD_PREFIX) && grp_msklen == STAR_STAR_RP_MSKLEN) rp_flag = TRUE; else rp_flag = FALSE; switch (join_prune) { case PIM_ACTION_JOIN: if (rp_flag == TRUE) data = bjpm->rp_list_join + bjpm->rp_list_join_size; else data = bjpm->join_list + bjpm->join_list_size; break; case PIM_ACTION_PRUNE: if (rp_flag == TRUE) data = bjpm->rp_list_prune + bjpm->rp_list_prune_size; else data = bjpm->prune_list + bjpm->prune_list_size; break; default: return FALSE; } flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */ if (addr_flags & MRTF_RP) flags |= USADDR_RP_BIT; if (addr_flags & MRTF_WC) flags |= USADDR_WC_BIT; PUT_ESADDR(source, src_msklen, flags, data); switch (join_prune) { case PIM_ACTION_JOIN: if (rp_flag == TRUE) { bjpm->rp_list_join_size = data - bjpm->rp_list_join; bjpm->rp_list_join_number++; } else { bjpm->join_list_size = data - bjpm->join_list; bjpm->join_addr_number++; } break; case PIM_ACTION_PRUNE: if (rp_flag == TRUE) { bjpm->rp_list_prune_size = data - bjpm->rp_list_prune; bjpm->rp_list_prune_number++; } else { bjpm->prune_list_size = data - bjpm->prune_list; bjpm->prune_addr_number++; } break; default: return FALSE; } return TRUE; } /* TODO: check again the size of the buffers */ static build_jp_message_t *get_jp_working_buff(void) { build_jp_message_t *bjpm; if (build_jp_message_pool_counter == 0) { bjpm = calloc(1, sizeof(build_jp_message_t)); if (!bjpm) return NULL; bjpm->next = NULL; bjpm->jp_message = calloc(1, MAX_JP_MESSAGE_SIZE + sizeof(pim_jp_encod_grp_t) + 2 * sizeof(pim_encod_src_addr_t)); if (!bjpm->jp_message) { free(bjpm); return NULL; } bjpm->jp_message_size = 0; bjpm->join_list_size = 0; bjpm->join_addr_number = 0; bjpm->join_list = calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm->join_list) { free(bjpm->jp_message); free(bjpm); return NULL; } bjpm->prune_list_size = 0; bjpm->prune_addr_number = 0; bjpm->prune_list = calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm->prune_list) { free(bjpm->join_list); free(bjpm->jp_message); free(bjpm); return NULL; } bjpm->rp_list_join_size = 0; bjpm->rp_list_join_number = 0; bjpm->rp_list_join = calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm->rp_list_join) { free(bjpm->prune_list); free(bjpm->join_list); free(bjpm->jp_message); free(bjpm); return NULL; } bjpm->rp_list_prune_size = 0; bjpm->rp_list_prune_number = 0; bjpm->rp_list_prune = calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm->rp_list_prune) { free(bjpm->rp_list_join); free(bjpm->prune_list); free(bjpm->join_list); free(bjpm->jp_message); free(bjpm); return NULL; } bjpm->curr_group = INADDR_ANY_N; bjpm->curr_group_msklen = 0; bjpm->holdtime = 0; return bjpm; } bjpm = build_jp_message_pool; build_jp_message_pool = build_jp_message_pool->next; build_jp_message_pool_counter--; bjpm->jp_message_size = 0; bjpm->join_list_size = 0; bjpm->join_addr_number = 0; bjpm->prune_list_size = 0; bjpm->prune_addr_number = 0; bjpm->curr_group = INADDR_ANY_N; bjpm->curr_group_msklen = 0; return bjpm; } static void return_jp_working_buff(pim_nbr_entry_t *pim_nbr) { build_jp_message_t *bjpm = pim_nbr->build_jp_message; if (!bjpm) return; /* Don't waste memory by keeping too many free buffers */ /* TODO: check/modify the definitions for POOL_NUMBER and size */ if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER) { free(bjpm->jp_message); free(bjpm->join_list); free(bjpm->prune_list); free(bjpm->rp_list_join); free(bjpm->rp_list_prune); free(bjpm); } else { bjpm->next = build_jp_message_pool; build_jp_message_pool = bjpm; build_jp_message_pool_counter++; } pim_nbr->build_jp_message = NULL; } /* TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the * Join/Prune message. However, this particular implementation of PIM * processes the Join/Prune messages faster if (*,*,RP) is at the beginning. * Modify some of the functions below such that the * outgoing messages place (*,*,RP) at the beginning, not at the end. */ static void pack_jp_message(pim_nbr_entry_t *pim_nbr) { build_jp_message_t *bjpm; uint8_t *data; bjpm = pim_nbr->build_jp_message; if (!bjpm || (bjpm->curr_group == INADDR_ANY_N)) return; data = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(bjpm->curr_group, bjpm->curr_group_msklen, 0, data); PUT_HOSTSHORT(bjpm->join_addr_number, data); PUT_HOSTSHORT(bjpm->prune_addr_number, data); memcpy(data, bjpm->join_list, bjpm->join_list_size); data += bjpm->join_list_size; memcpy(data, bjpm->prune_list, bjpm->prune_list_size); data += bjpm->prune_list_size; bjpm->jp_message_size = (data - bjpm->jp_message); bjpm->curr_group = INADDR_ANY_N; bjpm->curr_group_msklen = 0; bjpm->join_list_size = 0; bjpm->join_addr_number = 0; bjpm->prune_list_size = 0; bjpm->prune_addr_number = 0; (*bjpm->num_groups_ptr)++; if (*bjpm->num_groups_ptr == ((uint8_t)~0 - 1)) { if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) { /* Add the (*,*,RP) at the end */ data = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data); PUT_HOSTSHORT(bjpm->rp_list_join_number, data); PUT_HOSTSHORT(bjpm->rp_list_prune_number, data); memcpy(data, bjpm->rp_list_join, bjpm->rp_list_join_size); data += bjpm->rp_list_join_size; memcpy(data, bjpm->rp_list_prune, bjpm->rp_list_prune_size); data += bjpm->rp_list_prune_size; bjpm->jp_message_size = (data - bjpm->jp_message); bjpm->rp_list_join_size = 0; bjpm->rp_list_join_number = 0; bjpm->rp_list_prune_size = 0; bjpm->rp_list_prune_number = 0; (*bjpm->num_groups_ptr)++; } send_jp_message(pim_nbr); } } void pack_and_send_jp_message(pim_nbr_entry_t *pim_nbr) { uint8_t *data; build_jp_message_t *bjpm; if (!pim_nbr || !pim_nbr->build_jp_message) return; pack_jp_message(pim_nbr); bjpm = pim_nbr->build_jp_message; if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) { /* Add the (*,*,RP) at the end */ data = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data); PUT_HOSTSHORT(bjpm->rp_list_join_number, data); PUT_HOSTSHORT(bjpm->rp_list_prune_number, data); memcpy(data, bjpm->rp_list_join, bjpm->rp_list_join_size); data += bjpm->rp_list_join_size; memcpy(data, bjpm->rp_list_prune, bjpm->rp_list_prune_size); data += bjpm->rp_list_prune_size; bjpm->jp_message_size = (data - bjpm->jp_message); bjpm->rp_list_join_size = 0; bjpm->rp_list_join_number = 0; bjpm->rp_list_prune_size = 0; bjpm->rp_list_prune_number = 0; (*bjpm->num_groups_ptr)++; } send_jp_message(pim_nbr); } static void send_jp_message(pim_nbr_entry_t *pim_nbr) { size_t len; vifi_t vifi; len = pim_nbr->build_jp_message->jp_message_size; vifi = pim_nbr->vifi; memcpy(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t), pim_nbr->build_jp_message->jp_message, len); logit(LOG_INFO, 0, "Send PIM JOIN/PRUNE from %s on %s", inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)), uvifs[vifi].uv_name); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_JOIN_PRUNE, len); return_jp_working_buff(pim_nbr); } /************************************************************************ * PIM_ASSERT ************************************************************************/ int receive_pim_assert(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len) { vifi_t vifi; pim_encod_uni_addr_t eusaddr; pim_encod_grp_addr_t egaddr; uint32_t source, group; mrtentry_t *mrt, *mrt2; uint8_t *data; struct uvif *v; uint32_t assert_preference; uint32_t assert_metric; uint32_t assert_rptbit; uint32_t local_metric; uint32_t local_preference; uint8_t local_rptbit; uint8_t local_wins; pim_nbr_entry_t *original_upstream_router; vifi = find_vif_direct(src); if (vifi == NO_VIF) { /* Either a local vif or somehow received PIM_ASSERT from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) logit(LOG_DEBUG, 0, "Ignoring PIM_ASSERT from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } /* Checksum */ if (inet_cksum((uint16_t *)msg, len)) return FALSE; v = &uvifs[vifi]; if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ data = (uint8_t *)(msg + sizeof(pim_header_t)); /* Get the group and source addresses */ GET_EGADDR(&egaddr, data); GET_EUADDR(&eusaddr, data); /* Get the metric related info */ GET_HOSTLONG(assert_preference, data); GET_HOSTLONG(assert_metric, data); assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT; source = eusaddr.unicast_addr; group = egaddr.mcast_addr; logit(LOG_INFO, 0, "Received PIM ASSERT from %s for group %s and source %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3))); /* Find the longest "active" entry, i.e. the one with a kernel mirror */ if (assert_rptbit) { mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) { if (mrt->flags & MRTF_WC) mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink; } } else { mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) { if (mrt->flags & MRTF_SG) { mrt2 = mrt->group->grp_route; if (mrt2 && (mrt2->flags & MRTF_KERNEL_CACHE)) mrt = mrt2; else mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink; } else { if (mrt->flags & MRTF_WC) mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink; } } } if (!mrt || !(mrt->flags & MRTF_KERNEL_CACHE)) { /* No routing entry or not "active" entry. Ignore the assert */ return FALSE; } /* Prepare the local preference and metric */ if ((mrt->flags & MRTF_PMBR) || ((mrt->flags & MRTF_SG) && !(mrt->flags & MRTF_RP))) { /* Either (S,G) (toward S) or (*,*,RP). */ /* TODO: XXX: get the info from mrt, or source or from kernel ? */ /* local_metric = mrt->source->metric; local_preference = mrt->source->preference; */ local_metric = mrt->metric; local_preference = mrt->preference; } else { /* Should be (*,G) or (S,G)RPbit entry. * Get what we need from the RP info. */ /* TODO: get the info from mrt, RP-entry or kernel? */ /* local_metric = mrt->group->active_rp_grp->rp->rpentry->metric; local_preference = mrt->group->active_rp_grp->rp->rpentry->preference; */ local_metric = mrt->metric; local_preference = mrt->preference; } local_rptbit = (mrt->flags & MRTF_RP); if (local_rptbit) { /* Make the RPT bit the most significant one */ local_preference |= PIM_ASSERT_RPT_BIT; } if (VIFM_ISSET(vifi, mrt->oifs)) { /* The ASSERT has arrived on oif */ /* TODO: XXX: here the processing order is different from the spec. * The spec requires first eventually to create a routing entry * (see 3.5.2.1(1) and then compare the metrics. Here we compare * first the metrics with the existing longest match entry and * if we lose then create a new entry and compare again. This saves * us the unnecessary creating of a routing entry if we anyway are * going to lose: for example the local (*,*,RP) vs the remote * (*,*,RP) or (*,G) */ local_wins = compare_metrics(local_preference, local_metric, v->uv_lcl_addr, assert_preference, assert_metric, src); if (local_wins == TRUE) { /* TODO: verify the parameters */ send_pim_assert(source, group, vifi, mrt); return TRUE; } /* Create a "better" routing entry and try again */ if (assert_rptbit && (mrt->flags & MRTF_PMBR)) { /* The matching entry was (*,*,RP). Create (*,G) */ mrt2 = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); } else if (!assert_rptbit && (mrt->flags & (MRTF_WC | MRTF_PMBR))) { /* create (S,G) */ mrt2 = find_route(source, group, MRTF_SG, CREATE); } else { /* We have no chance to win. Give up and prune the oif */ mrt2 = NULL; } if (mrt2 && (mrt2->flags & MRTF_NEW)) { mrt2->flags &= ~MRTF_NEW; /* TODO: XXX: The spec doesn't say what entry timer value * to use when the routing entry is created because of asserts. */ SET_TIMER(mrt2->timer, PIM_DATA_TIMEOUT); if (mrt2->flags & MRTF_RP) { /* Either (*,G) or (S,G)RPbit entry. * Get what we need from the RP info. */ /* TODO: where to get the metric+preference from? */ /* local_metric = mrt->group->active_rp_grp->rp->rpentry->metric; local_preference = mrt->group->active_rp_grp->rp->rpentry->preference; */ local_metric = mrt->metric; local_preference = mrt->preference; local_preference |= PIM_ASSERT_RPT_BIT; } else { /* (S,G) toward the source */ /* TODO: where to get the metric from ? */ /* local_metric = mrt->source->metric; local_preference = mrt->source->preference; */ local_metric = mrt->metric; local_preference = mrt->preference; } local_wins = compare_metrics(local_preference, local_metric, v->uv_lcl_addr, assert_preference, assert_metric, src); if (local_wins == TRUE) { /* TODO: verify the parameters */ send_pim_assert(source, group, vifi, mrt); return TRUE; } /* We lost, but have created the entry which has to be pruned */ mrt = mrt2; } /* Have to remove that outgoing vifi from mrt */ VIFM_SET(vifi, mrt->asserted_oifs); mrt->flags |= MRTF_ASSERTED; if (mrt->assert_timer < PIM_ASSERT_TIMEOUT) SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT); /* TODO: XXX: check that the timer of all affected routing entries * has been restarted. */ change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); return FALSE; /* Doesn't matter the return value */ } /* End of assert received on oif */ if (mrt->incoming == vifi) { /* Assert received on iif */ if (assert_rptbit) { if (!(mrt->flags & MRTF_RP)) return TRUE; /* The locally used upstream router will * win the assert, so don't change it. */ } /* Ignore assert message if we do not have an upstream router */ if (mrt->upstream == NULL) return FALSE; /* TODO: where to get the local metric and preference from? * system call or mrt is fine? */ local_metric = mrt->metric; local_preference = mrt->preference; if (mrt->flags & MRTF_RP) local_preference |= PIM_ASSERT_RPT_BIT; local_wins = compare_metrics(local_preference, local_metric, mrt->upstream->address, assert_preference, assert_metric, src); if (local_wins == TRUE) return TRUE; /* return whatever */ /* The upstream must be changed to the winner */ mrt->preference = assert_preference; mrt->metric = assert_metric; mrt->upstream = find_pim_nbr(src); /* Check if the upstream router is different from the original one */ if (mrt->flags & MRTF_PMBR) { original_upstream_router = mrt->source->upstream; } else { if (mrt->flags & MRTF_RP) original_upstream_router = mrt->group->active_rp_grp->rp->rpentry->upstream; else original_upstream_router = mrt->source->upstream; } if (mrt->upstream != original_upstream_router) { mrt->flags |= MRTF_ASSERTED; SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT); } else { mrt->flags &= ~MRTF_ASSERTED; } } return TRUE; } int send_pim_assert(uint32_t source, uint32_t group, vifi_t vifi, mrtentry_t *mrt) { uint8_t *data; uint8_t *data_start; uint32_t local_preference; uint32_t local_metric; srcentry_t *srcentry __attribute__((unused)); /* Don't send assert if the outgoing interface a tunnel or register vif */ /* TODO: XXX: in the code above asserts are accepted over VIFF_TUNNEL. * Check if anything can go wrong if asserts are accepted and/or * sent over VIFF_TUNNEL. */ if (uvifs[vifi].uv_flags & (VIFF_REGISTER | VIFF_TUNNEL)) return FALSE; data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); data_start = data; PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data); PUT_EUADDR(source, data); /* TODO: XXX: where to get the metric from: srcentry or mrt * or from the kernel? */ if (mrt->flags & MRTF_PMBR) { /* (*,*,RP) */ srcentry = mrt->source; /* TODO: set_incoming(srcentry, PIM_IIF_RP); */ } else if (mrt->flags & MRTF_RP) { /* (*,G) or (S,G)RPbit (iif toward RP) */ srcentry = mrt->group->active_rp_grp->rp->rpentry; /* TODO: set_incoming(srcentry, PIM_IIF_RP); */ } else { /* (S,G) toward S */ srcentry = mrt->source; /* TODO: set_incoming(srcentry, PIM_IIF_SOURCE); */ } /* TODO: check again! local_metric = srcentry->metric; local_preference = srcentry->preference; */ local_metric = mrt->metric; local_preference = mrt->preference; if (mrt->flags & MRTF_RP) local_preference |= PIM_ASSERT_RPT_BIT; PUT_HOSTLONG(local_preference, data); PUT_HOSTLONG(local_metric, data); logit(LOG_INFO, 0, "Send PIM ASSERT from %s for group %s and source %s", inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3))); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_ASSERT, data - data_start); return TRUE; } /* Return TRUE if the local win, otherwise FALSE */ static int compare_metrics(uint32_t local_preference, uint32_t local_metric, uint32_t local_address, uint32_t remote_preference, uint32_t remote_metric, uint32_t remote_address) { /* Now lets see who has a smaller gun (aka "asserts war") */ /* FYI, the smaller gun...err metric wins, but if the same * caliber, then the bigger network address wins. The order of * threatment is: preference, metric, address. */ /* The RPT bits are already included as the most significant bits * of the preferences. */ if (remote_preference > local_preference) return TRUE; if (remote_preference < local_preference) return FALSE; if (remote_metric > local_metric) return TRUE; if (remote_metric < local_metric) return FALSE; if (ntohl(local_address) > ntohl(remote_address)) return TRUE; return FALSE; } /************************************************************************ * PIM_BOOTSTRAP ************************************************************************/ #define PIM_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN) int receive_pim_bootstrap(uint32_t src, uint32_t dst, char *msg, size_t len) { uint8_t *data; uint8_t *max_data; uint16_t new_bsr_fragment_tag; uint8_t new_bsr_hash_masklen; uint8_t new_bsr_priority; pim_encod_uni_addr_t new_bsr_uni_addr; uint32_t new_bsr_address; struct rpfctl rpfc; pim_nbr_entry_t *n, *rpf_neighbor __attribute__((unused)); uint32_t neighbor_addr; vifi_t vifi, incoming = NO_VIF; int min_datalen; pim_encod_grp_addr_t curr_group_addr; pim_encod_uni_addr_t curr_rp_addr; uint8_t curr_rp_count; uint8_t curr_frag_rp_count; uint16_t reserved_short __attribute__((unused)); uint16_t curr_rp_holdtime; uint8_t curr_rp_priority; uint8_t reserved_byte __attribute__((unused)); uint32_t curr_group_mask; uint32_t prefix_h; grp_mask_t *grp_mask; grp_mask_t *grp_mask_next; rp_grp_entry_t *grp_rp; rp_grp_entry_t *grp_rp_next; /* Checksum */ if (inet_cksum((uint16_t *)msg, len)) return FALSE; if (find_vif_direct(src) == NO_VIF) { /* Either a local vif or somehow received PIM_BOOTSTRAP from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) logit(LOG_DEBUG, 0, "Ignoring PIM_BOOTSTRAP from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } /* sanity check for the minimum length */ if (len < PIM_BOOTSTRAP_MINLEN) { logit(LOG_NOTICE, 0, "receive_pim_bootstrap: Bootstrap message size(%u) is too short from %s", len, inet_fmt(src, s1, sizeof(s1))); return FALSE; } data = (uint8_t *)(msg + sizeof(pim_header_t)); /* Parse the PIM_BOOTSTRAP message */ GET_HOSTSHORT(new_bsr_fragment_tag, data); GET_BYTE(new_bsr_hash_masklen, data); GET_BYTE(new_bsr_priority, data); GET_EUADDR(&new_bsr_uni_addr, data); new_bsr_address = new_bsr_uni_addr.unicast_addr; if (local_address(new_bsr_address) != NO_VIF) return FALSE; /* The new BSR is one of my local addresses */ /* * Compare the current BSR priority with the priority of the BSR * included in the message. */ /* TODO: if I am just starting and will become the BSR, * I should accept the message coming from the current BSR and get the * current Cand-RP-Set. */ if ((curr_bsr_priority > new_bsr_priority) || ((curr_bsr_priority == new_bsr_priority) && (ntohl(curr_bsr_address) > ntohl(new_bsr_address)))) { /* The message's BSR is less preferred than the current BSR */ return FALSE; /* Ignore the received BSR message */ } logit(LOG_INFO, 0, "Received PIM Bootstrap candidate %s, priority %d", inet_fmt(new_bsr_address, s1, sizeof(s1)), new_bsr_priority); /* Check the iif, if this was PIM-ROUTERS multicast */ if (dst == allpimrouters_group) { k_req_incoming(new_bsr_address, &rpfc); if (rpfc.iif == NO_VIF || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) { /* coudn't find a route to the BSR */ return FALSE; } neighbor_addr = rpfc.rpfneighbor.s_addr; incoming = rpfc.iif; if (uvifs[incoming].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) return FALSE; /* Shoudn't arrive on that interface */ /* Find the upstream router */ for (n = uvifs[incoming].uv_pim_neighbors; n; n = n->next) { if (ntohl(neighbor_addr) < ntohl(n->address)) continue; if (neighbor_addr == n->address) { rpf_neighbor = n; break; } return FALSE; /* No neighbor toward BSR found */ } if (!n || n->address != src) return FALSE; /* Sender of this message is not the RPF neighbor */ } else { if (local_address(dst) == NO_VIF) { /* TODO: XXX: this situation should be handled earlier: * The destination is neither ALL_PIM_ROUTERS neither me */ return FALSE; } /* Probably unicasted from the current DR */ if (cand_rp_list) { /* Hmmm, I do have a Cand-RP-list, but some neighbor has a * different opinion and is unicasting it to me. Ignore this guy. */ return FALSE; } for (vifi = 0; vifi < numvifs; vifi++) { if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; if (uvifs[vifi].uv_lcl_addr == dst) { incoming = vifi; break; } } if (incoming == NO_VIF) { /* Cannot find the receiving iif toward that DR */ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP) logit(LOG_DEBUG, 0, "Unicast boostrap message from %s to ignored: cannot find iif", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return FALSE; } /* TODO: check the sender is directly connected and I am really the DR */ } if (cand_rp_flag == TRUE) { /* If change in the BSR address, schedule immediate Cand-RP-Adv */ /* TODO: use some random delay? */ if (new_bsr_address != curr_bsr_address) SET_TIMER(pim_cand_rp_adv_timer, 0); } /* Forward the BSR Message first and then update the RP-set list */ /* TODO: if the message was unicasted to me, resend? */ for (vifi = 0; vifi < numvifs; vifi++) { if (vifi == incoming) continue; if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_NONBRS)) continue; memcpy(pim_send_buf + sizeof(struct ip), msg, len); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_BOOTSTRAP, len - sizeof(pim_header_t)); } max_data = (uint8_t *)msg + len; /* TODO: XXX: this 22 is HARDCODING!!! Do a bunch of definitions * and make it stylish! */ min_datalen = 22; if (new_bsr_fragment_tag != curr_bsr_fragment_tag || new_bsr_address != curr_bsr_address) { /* Throw away the old segment */ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list); } curr_bsr_address = new_bsr_address; curr_bsr_priority = new_bsr_priority; curr_bsr_fragment_tag = new_bsr_fragment_tag; MASKLEN_TO_MASK(new_bsr_hash_masklen, curr_bsr_hash_mask); SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT); while (data + min_datalen <= max_data) { GET_EGADDR(&curr_group_addr, data); GET_BYTE(curr_rp_count, data); GET_BYTE(curr_frag_rp_count, data); GET_HOSTSHORT(reserved_short, data); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); if (curr_rp_count == 0) { delete_grp_mask(&cand_rp_list, &grp_mask_list, curr_group_addr.mcast_addr, curr_group_mask); continue; } if (curr_rp_count == curr_frag_rp_count) { /* Add all RPs */ while (curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data); GET_HOSTSHORT(curr_rp_holdtime, data); GET_BYTE(curr_rp_priority, data); GET_BYTE(reserved_byte, data); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } continue; } /* * This is a partial list of the RPs for this group prefix. * Save until all segments arrive. */ prefix_h = ntohl(curr_group_addr.mcast_addr & curr_group_mask); for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask->next) { if (ntohl(grp_mask->group_addr & grp_mask->group_mask) > prefix_h) continue; break; } if (grp_mask && (grp_mask->group_addr == curr_group_addr.mcast_addr) && (grp_mask->group_mask == curr_group_mask) && (grp_mask->group_rp_number + curr_frag_rp_count == curr_rp_count)) { /* All missing PRs have arrived. Add all RP entries */ while (curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data); GET_HOSTSHORT(curr_rp_holdtime, data); GET_BYTE(curr_rp_priority, data); GET_BYTE(reserved_byte, data); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } /* Add the rest from the previously saved segments */ for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp->grp_rp_next) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp->rp->rpentry->address, grp_rp->priority, grp_rp->holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } delete_grp_mask(&segmented_cand_rp_list, &segmented_grp_mask_list, curr_group_addr.mcast_addr, curr_group_mask); } else { /* Add the partially received RP-list to the group of pending RPs*/ while (curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data); GET_HOSTSHORT(curr_rp_holdtime, data); GET_BYTE(curr_rp_priority, data); GET_BYTE(reserved_byte, data); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } } } /* Garbage collection. Check all group prefixes and if the * fragment_tag for a group-prefix is the same as curr_bsr_fragment_tag, * then remove all RPs for this group-prefix which have different * fragment tag. */ for (grp_mask = grp_mask_list; grp_mask; grp_mask = grp_mask_next) { grp_mask_next = grp_mask->next; if (grp_mask->fragment_tag == curr_bsr_fragment_tag) { for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) { grp_rp_next = grp_rp->grp_rp_next; if (grp_rp->fragment_tag != curr_bsr_fragment_tag) delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp); } } } /* Cleanup also the list used by incompleted segments */ for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask_next) { grp_mask_next = grp_mask->next; if (grp_mask->fragment_tag == curr_bsr_fragment_tag) { for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) { grp_rp_next = grp_rp->grp_rp_next; if (grp_rp->fragment_tag != curr_bsr_fragment_tag) delete_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, grp_rp); } } } return TRUE; } void send_pim_bootstrap(void) { size_t len; vifi_t vifi; if ((len = create_pim_bootstrap_message(pim_send_buf))) { for (vifi = 0; vifi < numvifs; vifi++) { if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_BOOTSTRAP, len); } } } /************************************************************************ * PIM_CAND_RP_ADV ************************************************************************/ /* * If I am the Bootstrap router, process the advertisement, otherwise * ignore it. */ #define PIM_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN) int receive_pim_cand_rp_adv(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len) { uint8_t prefix_cnt; uint8_t priority; uint16_t holdtime; pim_encod_uni_addr_t euaddr; pim_encod_grp_addr_t egaddr; uint8_t *data_ptr; uint32_t grp_mask; /* Checksum */ if (inet_cksum((uint16_t *)msg, len)) return FALSE; /* if I am not the bootstrap RP, then do not accept the message */ if (cand_bsr_flag == FALSE || curr_bsr_address != my_bsr_address) return FALSE; /* sanity check for the minimum length */ if (len < PIM_CAND_RP_ADV_MINLEN) { logit(LOG_NOTICE, 0, "%s(): cand_RP message size(%u) is too short from %s", __func__, len, inet_fmt(src, s1, sizeof(s1))); return FALSE; } data_ptr = (uint8_t *)(msg + sizeof(pim_header_t)); /* Parse the CAND_RP_ADV message */ /* TODO: XXX: check len whether it is at least the minimum */ GET_BYTE(prefix_cnt, data_ptr); GET_BYTE(priority, data_ptr); GET_HOSTSHORT(holdtime, data_ptr); GET_EUADDR(&euaddr, data_ptr); if (prefix_cnt == 0) { /* The default 224.0.0.0 and masklen of 4 */ MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, grp_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, euaddr.unicast_addr, priority, holdtime, htonl(ALL_MCAST_GROUPS_ADDR), grp_mask, my_bsr_hash_mask, curr_bsr_fragment_tag); return TRUE; } while (prefix_cnt--) { GET_EGADDR(&egaddr, data_ptr); MASKLEN_TO_MASK(egaddr.masklen, grp_mask); /* Do not advertise internal virtual RP for SSM groups */ if (!IN_PIM_SSM_RANGE(egaddr.mcast_addr)) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, euaddr.unicast_addr, priority, holdtime, egaddr.mcast_addr, grp_mask, my_bsr_hash_mask, curr_bsr_fragment_tag); } /* TODO: Check for len */ } return TRUE; } int send_pim_cand_rp_adv(void) { uint8_t prefix_cnt; uint32_t mask; pim_encod_grp_addr_t addr; uint8_t *data; if (!inet_valid_host(curr_bsr_address)) return FALSE; /* No BSR yet */ if (curr_bsr_address == my_bsr_address) { /* I am the BSR and have to include my own group-prefix stuff */ prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr; if (prefix_cnt == 0) { /* The default 224.0.0.0 and masklen of 4 */ MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, my_cand_rp_address, my_cand_rp_priority, my_cand_rp_holdtime, htonl(ALL_MCAST_GROUPS_ADDR), mask, my_bsr_hash_mask, curr_bsr_fragment_tag); return TRUE; } /* TODO: hardcoding!! */ data = cand_rp_adv_message.buffer + (4 + 6); while (prefix_cnt--) { GET_EGADDR(&addr, data); MASKLEN_TO_MASK(addr.masklen, mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, my_cand_rp_address, my_cand_rp_priority, my_cand_rp_holdtime, addr.mcast_addr, mask, my_bsr_hash_mask, curr_bsr_fragment_tag); /* TODO: Check for len */ } return TRUE; } data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); memcpy(data, cand_rp_adv_message.buffer, cand_rp_adv_message.message_size); send_pim_unicast(pim_send_buf, 0, my_cand_rp_address, curr_bsr_address, PIM_CAND_RP_ADV, cand_rp_adv_message.message_size); return TRUE; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/pimd.8000066400000000000000000000373761267035112600135140ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .Dd Mar 3, 2016 .\" Please adjust this date whenever revising the manpage. .Dt PIMD 8 SMM .Os .Sh NAME .Nm pimd .Nd PIM-SM/SSM v2 dynamic multicast routing daemon .Sh SYNOPSIS .Nm pimd .Op Fl fhlNqr .Op Fl c Ar FILE .Op Fl d Ar [SYS[,SYS,...] .Op Fl s Ar LEVEL .Sh DESCRIPTION .Nm is a lightweight, stand-alone PIM-SM/SSM v2 multicast routing daemon available under the free 3-clause BSD license. This is the restored original from University of Southern California, by Ahmed Helmy, Rusty Eddy and Pavlin Ivanov Radoslavov. .Pp Protocol Independent Multicast - Sparse Mode (PIM-SM): .Bl -bullet -width 1n -compact .It maintains the traditional IP multicast service model of receiver-initiated membership; .It uses explicit joins that propagate hop-by-hop from members' directly connected routers toward the distribution tree. .It builds a shared multicast distribution tree centered at a Rendezvous Point (RP), and then builds source-specific trees for those sources whose data traffic warrants it. .It is not dependent on a specific unicast routing protocol; and .It uses soft-state mechanisms to adapt to underlying network conditions and group dynamics. .El .Pp The robustness, flexibility, and scaling properties of this architecture make it well suited to large heterogeneous internetworks. .Pp .Nm originally only implemented RFC2362, but since v2.3.0 is supporting more and more of RFC4601. .Sh OPTIONS This program follows the usual UNIX command line syntax, with long options starting with two dashes (`-'). The options are as follows: .Bl -tag -width Ds .It Fl h, -help Print a help message and exit. .It Fl c, -config=FILE Specify an alternative configuration file, default .Pa /etc/pimd.conf . If .Nm cannot find its configuration file it will start up with fallback defaults, which include enabling both .Cm bsr-candidate and .Cm rp-candidate . .It Fl d, -debug[=SYS[,SYS...] By default, .Nm daemonizes itself by detaching from the invoking terminal and forking to the background. However, if .Fl d, -debug or .Fl f, -foreground is specified, .Nm runs in the foreground of the starting terminal. If .Fl d is given without any argument .Nm defaults to debug all subystems. .Pp Available subsystems are: .Pp .Bl -tag -width pim_routes -compact -offset indent .It Cm packet Debug inbound/outbout packets .It Cm prunes Pruning operations, or pruned routes .It Cm routes Routing messages .It Cm rtdetail Detailed routing information .It Cm peers Neighbor gossip .It Cm cache Debug routing cache .It Cm timeout Debug timeouts .It Cm interface Show interface (VIF) debug messages .It Cm groups Debug group memberships .It Cm mtrace Multicast traceroute information .It Cm igmp Debug IGMP messages .It Cm icmp Debug ICMP messages .It Cm rsrr Debug RSRR messages .It Cm pim All PIM messages .It Cm pim_routes PIM routing messages .It Cm pim_bsr PIM bootstrap router messages .It Cm pim_detail Detailed PIM debug .It Cm pim_hello Debug hello messages to/from neighbors .El .It Fl f, -foreground Run in the foreground, do not detach from calling terminal and do not fork to background. Useful not only when debugging (above) but also when running under a process monitor like daemontools, runit, finit, or systemd. .It Fl l, -reload-config Tell a running pimd to reload its configuration. This is done by sending a SIGHUP to the PID listed in .Pa /var/run/pimd.pid . Depending on the capabilities of your user, you may need to be root to do this. .It Fl N, -disable-vifs This prevents .Nm from being activated on all interfaces by default. When this command line option is given, use `phyint IFNAME enable` to selectively activate PIM services on an interface. .It Fl q, -quit-daemon Tell a running .Nm to quit. Similar to .Fl l, -reload-config but this command sends SIGTERM. Depending on the capabilities of your user, you may need to be root to do this. .It Fl r, -show-routes Show state of VIFs and multicast routing tables. This is command sends SIGUSR1 to a running .Nm , similar to .Fl l -reload-config. Depending on the capabilities of your user, you may need to be root to do this. .It Fl v, -version Show .Nm version .It Fl s, -loglevel=LEVEL Set log level to one of the following, default .Nm notice : .Pp .Bl -tag -width WARNING -compact -offset indent .It Cm none Disable all logging .It Cm error Error conditions .It Cm warning Warning conditions .It Cm notice Normal but significant condition (Default) .It Cm info Informational .It Cm debug Debug-level messages .El .El .Sh CONFIGURATION The configuration is kept in the file .Pa /etc/pimd.conf . The file format is relatively free-form: whitespace (including newlines) is not significant. However, the order of some statements are important, see more below. .Pp All arguments to an IPv4 address, group or network can also be given in the alternative /CIDR format. E.g., /. .Pp Here are the different configuration settings: .Bl -item -offset indent .It .Cm default-route-distance .Ar <1-255> .It .Cm default-route-metric .Ar <1-1024> .It .Cm igmp-query-interval .Ar <1-65535> .It .Cm igmp-querier-timeout .Ar <8-65535> .It .Cm hello-interval .Ar <30-18724> .It .Cm phyint .Cm
.Bl -item -offset indent .Op Cm disable | enable .Op Cm igmpv2 | igmpv3 .br .Op Cm dr-priority Ar <1-4294967294> .br .Op Cm ttl-threshold Ar <1-255> .Op Cm distance Ar <1-255> .Op Cm metric Ar <1-1024> .br .Oo .Cm altnet Ar Op Cm / | Cm masklen .Oc .br .Oo .Cm scoped Ar Op Cm / | Cm masklen .Oc .El .It .Cm bsr-candidate .Op Ar address | Ar ifname .Op Cm priority Ar .It .Cm rp-candidate .Op Ar address | Ar ifname .Op Cm priority Ar <0-255> .Op Cm time Ar <10-16384> .Bl -item -offset indent -compact .It .Cm group-prefix Ar [/ | Cm masklen Ar ] .It ... .It .Cm group-prefix ... .El .It .Cm rp-address Ar
[[/ | masklen | Cm packets Ar | Cm infinity .Op Cm interval Ar .El .Pp By default, .Nm will be activated on all multicast capable interfaces. The .Cm phyint setting and the .Fl N, -disable-vifs command line option control this behaviour. More on the .Cm phyint interface configuration setting below. .Pp The .Cm default-route-distance option has nothing to do with the system default route, it is rather the default value for the unicast routing protocol's administrative distance. It is used in PIM Assert elections to determine upstream routers. Currently .Nm cannot obtain the admin distance and metric from the unicast routing protocols, so a default routing protocol distance (the RFC confusingly refers to this as .Em metric prefererence ) may be configured. In a PIM Assert election, the router advertising the lowest assert preference will be selected as the forwarder and upstream router for the LAN. Setting 101 should be sufficiently high so that asserts from Cisco or GateD routers are preferred over poor-little pimd. .Pp It is reccommended that distances be set such that metrics are never consulted. However, default routing metrics may also be set using the .Cm default-route-metric option. (Again, this has nothing to do with the system default route.) This item sets the cost for sending data through this router. You want only PIM-SM data to go to this daemon; so once again, a high value is recommended to prevent accidental usage. The preferred default value is 1024. Both defaults can be overridden per phyint, so learned routes, or PIM Asserts use the phyint's values. .Pp Please also note that PIM Assert elections are not the same as the DR election. The PIM Assert election determines the active multicast forwarder, whereas the DR election determines the active PIM router. .Pp Two settings for IGMP behavior are available: .Cm igmp-query-interval and .Cm igmp-querier-timeout which are similar, but very different. The former controls the interval between IGMP querys when elected as querier, the latter controls the timeout for the elected querier -- before .Nm pimd decides to take over. In IGMP the lowest numerical address in a LAN becomes the elected querier. Obviously these settings must be handled with care. The RFC recommends that the querier timeout is set to a robustness value times the query interval, plus have the query response time. The pimd robustness value for IGMP is 3 and the default query response time is 10 sec. Since pimd v2.3.0 the default query interval is 12 sec, which makes the querier timeut default to 41 sec, but this is rounded off to 42 to honor the late Douglas Adams. .Pp The PIM Hello message interval can be tuned by changing the .Cm hello-interval setting. Changing this value also affects the hold-time value included in Hello messages. The hold-time value is 3.5 times hello-interval. The default value for the Hello interval is 30 sec. Anything less than 30 sec is considered an "aggressive" setting and is unsupported. .Pp The .Nm phyint option refers to a physical interface and must come after .Cm default-route-metric and .Cm default-route-distance . Select the interface either by its IP .Ar address or interface name .Ar ifname (e.g. eth0). If you just want to activate this interface with default values, you don't need to put anything else on the line. However, there are some additional settings: .Bl -bullet -offset indent -width 1n -compact .It .Nm disable : Do not send PIM-SM traffic through this interface nor listen for PIM-SM traffic from this interface. Default: enable. .Nm enable : Selectively enable which interfaces to send PIM-SM traffic through. Useful with the .Fl N command line option. .It .Nm igmpv2 : Force interface to use IGMPv2, or .It .Nm igmpv3 : Use IGMPv3, this is the default since v2.3.0. .It .Cm dr-priority Ar <1-4294967294> : When there are multiple PIM routers on the same LAN the DR is usually elected based on the highest numerical IP address. This setting can be used to control the DR Priority option in PIM Hellow messages, which by default otherwise is 1. When the DR Priority option is advertised by .Em all PIM routers on the same LAN the highest priority router wins the DR election, regardless of its IP. If any router does .Em not advertise the DR Priority option, or the same priority is advertised by more than one router, the protocol falls back to using the IP address. .It .Cm ttl-threshold Ar <1-255> : The TTL threshold for multicast frames to be forwarded from this interface. Default: 1 .It .Cm distance Ar <1-255> : Use this to override the .Nm default-route-distance (101) on this .Nm phyint in PIM Assert elections. .It .Cm metric Ar <1-1024> : The cost of sending data through this interface. Defaults to .Nm default-route-metric (1024) if not assigned. .It .Cm altnet Ar : Alternative host(s)/network(s) to accept as locally attached multicast sources on a given interface. If a phyint is attached to multiple IP subnets, describe each additional subnet with the altnet keyword. .It .Cm scoped Ar : Optional scoping of multicast groups. This allows interfaces to be configured as an administrative boundary for the specified group(s). Multicast streams belonging to the scoped groups will not be forwarded. .El .Pp Add one .Nm phyint line per interface on this router. If you don't do this, .Nm pimd will either be completely silent (if you provide the .Fl N command line option), or simply assume that you want it to utilize all interfaces using default settings. .Pp Both the .Cm bsr-candidate (CBSR) and .Cm rp-candidate (CRP) settings are enabled in the default configuration. Disabling them, by commenting them out in the config file, for all PIM capable routers is a bad idea. When troubleshooting, ensure at least one bootstrap router (BSR) and at least one rendez-vous point (RP) in PIM-SM, is available. Both settings share the following options, with priority being interpreted differently: .Pp .Bl -bullet -offset indent -width 1n -compact .It .Nm address | ifname : Optional local IPv4 address, or interface name to acquire address from. If both address and ifname is left out, .Nm will default to the highest active IP address. .It .Nm priority Ar <0-255> : How important this router is compared to others. For CRP, the lower the value the more important the router is considered. For BSR it is of course the exact opposite: a higher value is preferred. If the priority is left out .Nm and Cisco IOS defaults to 0 for both, but the standard says 192 for RP. .It .Nm time Ar <10-16383> : The number of seconds to wait between advertising this CRP. The default value is 30 seconds. Use a lower value for faster convergence. .El .Pp .Bl -item -offset indent -compact .It The .Nm group-prefix sub-setting to .Nm rp-candidate is the set of multicast groups that the CRP will advertise to other routers, if it wins an election: .Bl -bullet -offset indent -width 1n -compact .It .Nm group : A specific multicast group or network range this router will handle. .It .Nm masklen : Optional number of groups, in prefix length format. Remember that a multicast address is a Class D and has a netmask of 240.0.0.0, which means its length is 4. .El .Pp Multiple lines of .Nm group-prefix may be given, but max number of records supported in pimd is 255. .El .Pp The .Nm rp-address setting is for static rendezvous point (RP) configurations. It defines the RP for a given group, or range or groups. The argument can be either a unicast address or a multicast group, with an optional group address and netmask. Default group and netmask is 224.0.0.0/16. .Nm Note: all static RP's are announced with priority 1. .Pp The .Nm spt-threshold setting replaces two older configuration settings, .Nm switch_data_threshold and .Nm switch_register_threshold . It controls the switch-over from the shared tree to the shortest-path source tree. The default is to do the switch-over after the first packet, but only after 100 seconds. If .Ar infinity is specified the shortest path switch-over is disabled. .Sh SIGNALS .Nm responds to the following signals: .Pp .Bl -tag -width TERM -compact .It HUP Restarts .Nm . The configuration file is reread every time this signal is evoked. .It TERM Terminates execution gracefully (i.e. by sending good-bye messages to all neighboring routers). .It INT The same as TERM. .It USR1 Dumps the internal state of VIFs and multicast routing tables to .Pa /var/run/pimd/pimd.dump . See also the .Fl r, -show-routes option above. .\" Not implemented yet, still TODO .\" .It USR2 .\" Dumps the internal cache tables to .\" .Pa /var/run/pimd/pimd.cache . .\" Also not implemented yet, TODO .\" .It QUIT .\" Dumps the internal routing tables to stderr (only if .\" .Nm .\" was invoked with a non-zero debug level). .El .Pp For convenience in sending signals, .Nm writes its process ID to .Pa /var/run/pimd.pid upon startup. .Sh FILES .Bl -tag -width /var/run/pimd/pimd.cache -compact .It Pa /etc/pimd.conf .\" .It Pa /var/run/pimd/pimd.cache .It Pa /var/run/pimd/pimd.dump .It Pa /var/run/pimd.pid .El .Sh SEE ALSO .Xr mrouted 8 , .Xr smcroute 8 , .Xr /usr/share/doc/pimd/ .Pp PIM-SM is described in, the now obsolete RFC2362, and the current RFC4601, with additions in RFC5059 and RFC5796. .Pp The pages at USC, http://netweb.usc.edu/pim/, are unfortunately no longer available. The wiki pages at http://github.com/troglobit/pimd/, the new GitHub project, are an attempt to gather as much info as possible. .Sh AUTHORS .Nm was written by Ahmed Helmy, George Edmond "Rusty" Eddy, and Pavlin Ivanov Radoslavov. PIM-SSM, including full IGMPv3 support, added by Markus Veranen. With contributions by many others. .Pp This manual page was initially written by Antonín Král for the Debian GNU/Linux system, and then updated by Joachim Nilsson for the GitHub pimd project. pimd-2.3.2/pimd.conf000066400000000000000000000144431267035112600142600ustar00rootroot00000000000000# Exmaple configuration file for pimd, the original PIM-SM router # # See the pimd(8) man page for details on all the settings. This file # only gives very brief examples and is intended as a quick start. # # NOTE: The order of the settings matter! # ## # default-route-distance <1-255> # default-route-metric <1-1024> # hello-interval <30-18724> # # igmp-query-interval # igmp-querier-timeout # # phyint # [disable | enable] [igmpv2 | igmpv3] # [dr-priority <1-4294967294>] # [ttl-threshold <1-255>] [distance <1-255>] [metric <1-1024>] # [altnet [/ | masklen ]] # [scoped [/ | masklen ]] # # bsr-candidate [local-addr | ifname] [priority <0-255>] # rp-candidate [local-addr | ifname] [priority <0-255> ] [time <10-16383>] # group-prefix [/ | masklen ] # group-prefix [/ | masklen ] # . # . # group-prefix [/ | masklen ] # rp-address [[/ | masklen ] # # spt-threshold [rate | packets | infinity] [interval ] ## # # By default PIM is activated on all interfaces. Use `phyint disable` # on interfaces where PIM should not run. You can also use the `-N, # --disable-vifs` command line option along with `enable` to get the # inverse behavior. # # The routing protocol admin distance (or metric preference per the RFC) # is used in PIM Assert elections to elect the forwarder of multicast. # Currently pimd cannot obtain distance and metric from the underlying # routing protocols, so a default distance may need to be configured per # interface. If left out, the default-route-distance is used for the # phyint. In PIM assert elections the router advertising the lowest # preference (distance) will be selected as forwarder (upstream router) # for that LAN. An admin distance of 101 should be sufficiently high so # that asserts from Cisco or GateD routers are prefered over poor-little # pimd. # # It is reccommended that preferences (admin distance) be set such that # metrics are never consulted. However, default metrics may also be set # and default to 1024. # # A phyint directive can use either the interface name, ifname, or the # IP address. The distance and metric settings define administrative # distance and metric, respectively, for PIM Assert messages sent on # that interface. Usually you do not need this, but if you do, think of # them like distance and metric defined on an inbound interface (iif), # but used by PIM Asserts on the outbound interfaces (oifs). # # If you want to add "alternative (sub)net" to a physical interface, # e.g., if you want to make incoming traffic with a non-local source address # to appear as it is coming from a local subnet, then use the command: # # phyint altnet masklen # # NOTE: if you use this command, make sure you know what you are doing! # # If you want administratively scoped multicast filtering, use the # following command: # # phyint scoped masklen # # This allows interfaces to be configured as an administrative boundary # for the specified scoped address, or address range. Packets belonging # to the scoped range will not be forwarded. Use `--enable-scoped-acls` # flag to the configure script to activate this at build time. # # Both rp-candidate and bsr-candidate are enabled in the default config, # below. Disabling them for all PIM capable routers is a bad idea. At # least one PIM router in the backbone must act as a bootstrap router. # The optional local-addr or ifname arguments after the rp-candidate and # bsr-candidate settings specify the local address to be used in the # Cand-RP and Cand-BSR messages. In case ifname is given as argument, # the first IPv4 address of that interface is used. If either is # unspecified, the largest local IP address will be used, excluding # phyint interfaces where PIM has been disabled. # # The time argument to rp-candidate specifies how often to send Cand-RP # messages. The default value is 30 seconds. Use smaller values for # faster convergence. # # The group-prefix setting is the prefix(es) advertised if rp-candidate. # It is possible to set up to 255 group-prefix records. # # Using the rp-address setting it is possible to set a static rendezvous # point. The argument can be either a unicast or a multicast address # followed by an optional group address and optional masklen to that. # # The spt-threshold specifies the minimum rate in kbps before the last # hop router initiates a switch to the shortest path. The `packets` # argument is an alternative notation, `infinity` means to never switch, # and `interval` specifies the interval for periodical testing of the # threshold. Currently, `interval` must be at least 5 (seconds) # # Interface defaults, like default-route-distance and -metric must be # set before the phyint section -- the .conf parser is not clever. #default-route-distance 101 # smaller is better #default-route-metric 1024 # smaller is better #hello-interval 30 # Don't set lower than 30 # The phyint settings currently *MUST BE* ordered after the default # source preference and metric settings, but before everything else. # By default, all non-loopback multicast capable interfaces are enabled. # If you want to use loopback, set the interface multicast flag on it. #phyint eth0 disable # IGMP default query interval and querier timeout. The latter should # per RFC always be (robustness * interval) + (query-response / 2), for # pimd this means: (3 * 12) + (10 / 2) = 41, we've rounded it up to # honor the late Douglas Adams. You can set it to a higher value, but # it is not recommended to set it lower. #igmp-query-interval 12 #igmp-querier-timeout 42 # Bigger value means "higher" priority bsr-candidate priority 5 # Smaller value means "higher" priority rp-candidate time 30 priority 20 # Candidate for being RP of complete IPv4 multicast range #group-prefix 224.0.0.0 masklen 4 # Static rendez-vous point #rp-address 192.168.10.1 224.0.0.0/4 # Switch to shortest-path tree after first packet, but only after 100 sec. spt-threshold packets 0 interval 100 pimd-2.3.2/pimd.h000066400000000000000000000470351267035112600135650ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: pimd.h,v 1.27 2003/05/21 10:40:28 pavlin Exp $ */ #include #include #define PIM_PROTOCOL_VERSION 2 #define PIMD_VERSION PIM_PROTOCOL_VERSION #define PIMD_SUBVERSION 1 #if 0 #define PIM_CONSTANT 0x000eff00 /* constant portion of 'group' field */ #endif #define PIM_CONSTANT 0 #define PIMD_LEVEL (PIM_CONSTANT | PIMD_VERSION | (PIMD_SUBVERSION << 8)) #define INADDR_ALL_PIM_ROUTERS (uint32_t)0xe000000D /* 224.0.0.13 */ #if !defined(INADDR_UNSPEC_GROUP) #define INADDR_UNSPEC_GROUP (uint32_t)0xe0000000 /* 224.0.0.0 */ #endif /* !defined(INADDR_UNSPEC_GROUP) */ /* PIM protocol timers (in seconds) */ #ifndef TIMER_INTERVAL #define TIMER_INTERVAL 5 /* virtual timer granularity */ #endif /* TIMER_INTERVAL */ #define PIM_REGISTER_SUPPRESSION_TIMEOUT 60 #define PIM_REGISTER_PROBE_TIME 5 /* Used to send NULL_REGISTER */ #define PIM_DATA_TIMEOUT 210 #define PIM_TIMER_HELLO_INTERVAL 30 #define PIM_JOIN_PRUNE_PERIOD 60 #define PIM_JOIN_PRUNE_HOLDTIME (3.5 * PIM_JOIN_PRUNE_PERIOD) #define PIM_RANDOM_DELAY_JOIN_TIMEOUT 4.5 /* TODO: XXX: cannot be shorter than 10 seconds (not in the spec) * MAX: Cisco max value (16383) for ip pim rp-candidate interval. */ #define PIM_MIN_CAND_RP_ADV_PERIOD 10 #define PIM_DEFAULT_CAND_RP_ADV_PERIOD 60 #define PIM_MAX_CAND_RP_ADV_PERIOD 16383 /* TODO: 60 is the original value. Temporarily set to 30 for debugging. #define PIM_BOOTSTRAP_PERIOD 60 */ #define PIM_BOOTSTRAP_PERIOD 30 #define PIM_BOOTSTRAP_TIMEOUT (2.5 * PIM_BOOTSTRAP_PERIOD + 10) #define PIM_TIMER_HELLO_HOLDTIME (3.5 * PIM_TIMER_HELLO_INTERVAL) #define PIM_ASSERT_TIMEOUT 180 /* Misc definitions */ #define PIM_DEFAULT_CAND_RP_PRIORITY 0 /* 0 is the highest. Don't know * why this is the default. * See the PS version (Mar' 97), * pp.22 bottom of the spec. */ #define PIM_MAX_CAND_RP_PRIORITY 255 /* 255 is the highest. */ #define PIM_DEFAULT_BSR_PRIORITY 0 /* 0 is the lowest */ #define PIM_MAX_CAND_BSR_PRIORITY PIM_MAX_CAND_RP_PRIORITY #define RP_DEFAULT_IPV4_HASHMASKLEN 30 /* the default group msklen used * by the hash function to * calculate the group-to-RP * mapping */ #define SINGLE_SRC_MSKLEN 32 /* the single source mask length */ #define SINGLE_GRP_MSKLEN 32 /* the single group mask length */ #define PIM_GROUP_PREFIX_DEFAULT_MASKLEN 16 /* The default group masklen if * omitted in the config file. * XXX: not set to 4, because * a small mis-configuration * may concentrate all multicast * traffic in a single RP */ #define PIM_GROUP_PREFIX_MIN_MASKLEN 4 /* group prefix minimum length */ /* Datarate related definitions */ /* * This is the threshold for the last hop router, or the RP, to * initiate switching to the shortest path tree. Like Cisco we * change to SPT on first packet to the (S,G), but only after * 100 seconds (Xorp does this and Pavlin knows his PIM-SM-FU) */ #define SPT_THRESHOLD_DEFAULT_MODE SPT_PACKETS #define SPT_THRESHOLD_DEFAULT_RATE 0 #define SPT_THRESHOLD_DEFAULT_PACKETS 0 #define SPT_THRESHOLD_MAX_PACKETS 0 #define SPT_THRESHOLD_DEFAULT_INTERVAL 100 #define UCAST_ROUTING_CHECK_INTERVAL 20 /* Unfortunately, if the unicast * routing changes, the kernel * or any of the existing * unicast routing daemons * don't send us a signal. * Have to ask periodically the * kernel for any route changes. * Default: every 20 seconds. * Sigh. */ #define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */ #define DEFAULT_REG_RATE_LIMIT 0 /* default register_vif rate limit */ /************************************************************************** * PIM Encoded-Unicast, Encoded-Group and Encoded-Source Address formats * *************************************************************************/ /* Address families definition */ #define ADDRF_RESERVED 0 #define ADDRF_IPv4 1 #define ADDRF_IPv6 2 #define ADDRF_NSAP 3 #define ADDRF_HDLC 4 #define ADDRF_BBN1822 5 #define ADDRF_802 6 #define ADDRF_ETHERNET ADDRF_802 #define ADDRF_E163 7 #define ADDRF_E164 8 #define ADDRF_SMDS ADDRF_E164 #define ADDRF_ATM ADDRF_E164 #define ADDRF_F69 9 #define ADDRF_TELEX ADDRF_F69 #define ADDRF_X121 10 #define ADDRF_X25 ADDRF_X121 #define ADDRF_IPX 11 #define ADDRF_APPLETALK 12 #define ADDRF_DECNET_IV 13 #define ADDRF_BANYAN 14 #define ADDRF_E164_NSAP 15 /* Addresses Encoding Type (specific for each Address Family */ #define ADDRT_IPv4 0 /* Encoded-Unicast: 6 bytes long */ typedef struct pim_encod_uni_addr_ { uint8_t addr_family; uint8_t encod_type; uint32_t unicast_addr; /* XXX: Note the 32-bit boundary * misalignment for the unicast * address when placed in the * memory. Must read it byte-by-byte! */ } pim_encod_uni_addr_t; #define PIM_ENCODE_UNI_ADDR_LEN 6 /* Encoded-Group */ typedef struct pim_encod_grp_addr_ { uint8_t addr_family; uint8_t encod_type; uint8_t reserved; uint8_t masklen; uint32_t mcast_addr; } pim_encod_grp_addr_t; #define PIM_ENCODE_GRP_ADDR_LEN 8 /* Encoded-Source */ typedef struct pim_encod_src_addr_ { uint8_t addr_family; uint8_t encod_type; uint8_t flags; uint8_t masklen; uint32_t src_addr; } pim_encod_src_addr_t; #define PIM_ENCODE_SRC_ADDR_LEN 8 #define USADDR_RP_BIT 0x1 #define USADDR_WC_BIT 0x2 #define USADDR_S_BIT 0x4 /************************************************************************** * PIM Messages formats * *************************************************************************/ /* TODO: XXX: some structures are probably not used at all */ typedef struct pim pim_header_t; /* PIM Hello */ typedef struct pim_hello_ { uint16_t option_type; /* Option type */ uint16_t option_length; /* Length of the Option Value field in bytes */ } pim_hello_t; /* PIM Register */ typedef struct pim_register_ { uint32_t reg_flags; } pim_register_t; /* PIM Register-Stop */ typedef struct pim_register_stop_ { pim_encod_grp_addr_t encod_grp; pim_encod_uni_addr_t encod_src; /* XXX: 6 bytes long, misaligned */ } pim_register_stop_t; /* PIM Join/Prune: XXX: all 32-bit addresses misaligned! */ typedef struct pim_jp_header_ { pim_encod_uni_addr_t encod_upstream_nbr; uint8_t reserved; uint8_t num_groups; uint16_t holdtime; } pim_jp_header_t; typedef struct pim_jp_encod_grp_ { pim_encod_grp_addr_t encod_grp; uint16_t number_join_src; uint16_t number_prune_src; } pim_jp_encod_grp_t; #define PIM_ACTION_NOTHING 0 #define PIM_ACTION_JOIN 1 #define PIM_ACTION_PRUNE 2 #define PIM_IIF_SOURCE 1 #define PIM_IIF_RP 2 #define PIM_ASSERT_RPT_BIT 0x80000000 /* PIM messages type */ #ifndef PIM_HELLO #define PIM_HELLO 0 #endif #ifndef PIM_REGISTER #define PIM_REGISTER 1 #endif #ifndef PIM_REGISTER_STOP #define PIM_REGISTER_STOP 2 #endif #ifndef PIM_JOIN_PRUNE #define PIM_JOIN_PRUNE 3 #endif #ifndef PIM_BOOTSTRAP #define PIM_BOOTSTRAP 4 #endif #ifndef PIM_ASSERT #define PIM_ASSERT 5 #endif #ifndef PIM_GRAFT #define PIM_GRAFT 6 #endif #ifndef PIM_GRAFT_ACK #define PIM_GRAFT_ACK 7 #endif #ifndef PIM_CAND_RP_ADV #define PIM_CAND_RP_ADV 8 #endif #define PIM_V2_HELLO PIM_HELLO #define PIM_V2_REGISTER PIM_REGISTER #define PIM_V2_REGISTER_STOP PIM_REGISTER_STOP #define PIM_V2_JOIN_PRUNE PIM_JOIN_PRUNE #define PIM_V2_BOOTSTRAP PIM_BOOTSTRAP #define PIM_V2_ASSERT PIM_ASSERT #define PIM_V2_GRAFT PIM_GRAFT #define PIM_V2_GRAFT_ACK PIM_GRAFT_ACK #define PIM_V2_CAND_RP_ADV PIM_CAND_RP_ADV #define PIM_V1_QUERY 0 #define PIM_V1_REGISTER 1 #define PIM_V1_REGISTER_STOP 2 #define PIM_V1_JOIN_PRUNE 3 #define PIM_V1_RP_REACHABILITY 4 #define PIM_V1_ASSERT 5 #define PIM_V1_GRAFT 6 #define PIM_V1_GRAFT_ACK 7 /* Vartious options from PIM messages definitions */ /* PIM_HELLO definitions */ #define PIM_HELLO_HOLDTIME 1 #define PIM_HELLO_HOLDTIME_LEN 2 #define PIM_HELLO_HOLDTIME_FOREVER 0xffff #define PIM_HELLO_DR_PRIO 19 #define PIM_HELLO_DR_PRIO_LEN 4 #define PIM_HELLO_DR_PRIO_DEFAULT 1 #define PIM_HELLO_GENID 20 #define PIM_HELLO_GENID_LEN 4 /* PIM_REGISTER definitions */ #define PIM_REGISTER_BORDER_BIT 0x80000000 #define PIM_REGISTER_NULL_REGISTER_BIT 0x40000000 #define MASK_TO_MASKLEN(mask, masklen) \ do { \ uint32_t tmp_mask = ntohl((mask)); \ uint8_t tmp_masklen = sizeof((mask)) << 3; \ for ( ; tmp_masklen > 0; tmp_masklen--, tmp_mask >>= 1) \ if (tmp_mask & 0x1) \ break; \ (masklen) = tmp_masklen; \ } while (0) #define MASKLEN_TO_MASK(masklen, mask) \ do { \ (mask) = masklen ? htonl(~0U << ((sizeof(mask) << 3) - (masklen))) : 0; \ } while (0) /* * A bunch of macros because of the lack of 32-bit boundary alignment. * All because of one misalligned address format. Hopefully this will be * fixed in PIMv3. (cp) must be (uint8_t *) . */ /* Originates from Eddy Rusty's (eddy@isi.edu) PIM-SM implementation for * gated. */ #include #ifdef HOST_OS_WINDOWS #define LITTLE_ENDIAN 1234 #define BYTE_ORDER LITTLE_ENDIAN #endif /* HOST_OS_WINDOWS */ #if !defined(BYTE_ORDER) #if defined(__BYTE_ORDER) #define BYTE_ORDER __BYTE_ORDER #define LITTLE_ENDIAN __LITTLE_ENDIAN #define BIG_ENDIAN __BIG_ENDIAN #else #error "BYTE_ORDER not defined! Define it to either LITTLE_ENDIAN (e.g. i386, vax) or BIG_ENDIAN (e.g. 68000, ibm, net) based on your architecture!" #endif #endif /* PUT_NETLONG puts "network ordered" data to the datastream. * PUT_HOSTLONG puts "host ordered" data to the datastream. * GET_NETLONG gets the data and keeps it in "network order" in the memory * GET_HOSTLONG gets the data, but in the memory it is in "host order" * The same for all {PUT,GET}_{NET,HOST}{SHORT,LONG} */ #define GET_BYTE(val, cp) ((val) = *(cp)++) #define PUT_BYTE(val, cp) (*(cp)++ = (uint8_t)(val)) #define GET_HOSTSHORT(val, cp) \ do { \ uint16_t Xv; \ Xv = (*(cp)++) << 8; \ Xv |= *(cp)++; \ (val) = Xv; \ } while (0) #define PUT_HOSTSHORT(val, cp) \ do { \ uint16_t Xv; \ Xv = (uint16_t)(val); \ *(cp)++ = (uint8_t)(Xv >> 8); \ *(cp)++ = (uint8_t)Xv; \ } while (0) #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) #define GET_NETSHORT(val, cp) \ do { \ uint16_t Xv; \ Xv = *(cp)++; \ Xv |= (*(cp)++) << 8; \ (val) = Xv; \ } while (0) #define PUT_NETSHORT(val, cp) \ do { \ uint16_t Xv; \ Xv = (uint16_t)(val); \ *(cp)++ = (uint8_t)Xv; \ *(cp)++ = (uint8_t)(Xv >> 8); \ } while (0) #else #define GET_NETSHORT(val, cp) GET_HOSTSHORT(val, cp) #define PUT_NETSHORT(val, cp) PUT_HOSTSHORT(val, cp) #endif /* {GET,PUT}_NETSHORT */ #define GET_HOSTLONG(val, cp) \ do { \ uint32_t Xv; \ Xv = (*(cp)++) << 24; \ Xv |= (*(cp)++) << 16; \ Xv |= (*(cp)++) << 8; \ Xv |= *(cp)++; \ (val) = Xv; \ } while (0) #define PUT_HOSTLONG(val, cp) \ do { \ uint32_t Xv; \ Xv = (uint32_t)(val); \ *(cp)++ = (uint8_t)(Xv >> 24); \ *(cp)++ = (uint8_t)(Xv >> 16); \ *(cp)++ = (uint8_t)(Xv >> 8); \ *(cp)++ = (uint8_t)Xv; \ } while (0) #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) #define GET_NETLONG(val, cp) \ do { \ uint32_t Xv; \ Xv = *(cp)++; \ Xv |= (*(cp)++) << 8; \ Xv |= (*(cp)++) << 16; \ Xv |= (*(cp)++) << 24; \ (val) = Xv; \ } while (0) #define PUT_NETLONG(val, cp) \ do { \ uint32_t Xv; \ Xv = (uint32_t)(val); \ *(cp)++ = (uint8_t)Xv; \ *(cp)++ = (uint8_t)(Xv >> 8); \ *(cp)++ = (uint8_t)(Xv >> 16); \ *(cp)++ = (uint8_t)(Xv >> 24); \ } while (0) #else #define GET_NETLONG(val, cp) GET_HOSTLONG(val, cp) #define PUT_NETLONG(val, cp) PUT_HOSTLONG(val, cp) #endif /* {GET,PUT}_HOSTLONG */ #define GET_ESADDR(esa, cp) \ do { \ (esa)->addr_family = *(cp)++; \ (esa)->encod_type = *(cp)++; \ (esa)->flags = *(cp)++; \ (esa)->masklen = *(cp)++; \ GET_NETLONG((esa)->src_addr, (cp)); \ } while(0) #define PUT_ESADDR(addr, masklen, flags, cp) \ do { \ uint32_t mask; \ MASKLEN_TO_MASK((masklen), mask); \ *(cp)++ = ADDRF_IPv4; /* family */ \ *(cp)++ = ADDRT_IPv4; /* type */ \ *(cp)++ = (flags); /* flags */ \ *(cp)++ = (masklen); \ PUT_NETLONG((addr) & mask, (cp)); \ } while(0) #define GET_EGADDR(ega, cp) \ do { \ (ega)->addr_family = *(cp)++; \ (ega)->encod_type = *(cp)++; \ (ega)->reserved = *(cp)++; \ (ega)->masklen = *(cp)++; \ GET_NETLONG((ega)->mcast_addr, (cp)); \ } while(0) #define PUT_EGADDR(addr, masklen, reserved, cp) \ do { \ uint32_t mask; \ MASKLEN_TO_MASK((masklen), mask); \ *(cp)++ = ADDRF_IPv4; /* family */ \ *(cp)++ = ADDRT_IPv4; /* type */ \ *(cp)++ = (reserved); /* reserved; should be 0 */ \ *(cp)++ = (masklen); \ PUT_NETLONG((addr) & mask, (cp)); \ } while(0) #define GET_EUADDR(eua, cp) \ do { \ (eua)->addr_family = *(cp)++; \ (eua)->encod_type = *(cp)++; \ GET_NETLONG((eua)->unicast_addr, (cp)); \ } while(0) #define PUT_EUADDR(addr, cp) \ do { \ *(cp)++ = ADDRF_IPv4; /* family */ \ *(cp)++ = ADDRT_IPv4; /* type */ \ PUT_NETLONG((addr), (cp)); \ } while(0) /* Check if group is in PIM-SSM range, x must be in network byte order */ #define IN_PIM_SSM_RANGE(x) ((ntohl((unsigned)(x)) & 0xff000000) == 0xe8000000) /* Check if address is in link-local range, x must be in network byte order */ #define IN_LINK_LOCAL_RANGE(x) ((ntohl((unsigned)(x)) & 0xffff0000) == 0xa9fe0000) /* TODO: Currently not used. Probably not need at all. Delete! */ #if 0 /* This is completely IGMP related stuff? */ #define PIM_LEAF_TIMEOUT (3.5 * IGMP_QUERY_INTERVAL) #define PIM_REGISTER_BIT_TIMEOUT 30 /* TODO: check if need to be 60 */ #define PIM_ASSERT_RATE_TIMER 15 #endif /* 0 */ #if (defined(__bsdi__) || defined(NetBSD) || defined(OpenBSD) || defined(IRIX)) /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IP packet */ struct igmpmsg { uint32_t unused1; uint32_t unused2; uint8_t im_msgtype; /* what type of message */ #define IGMPMSG_NOCACHE 1 #define IGMPMSG_WRONGVIF 2 #define IGMPMSG_WHOLEPKT 3 /* used for user level encap*/ uint8_t im_mbz; /* must be zero */ uint8_t im_vif; /* vif rec'd on */ uint8_t unused3; struct in_addr im_src, im_dst; }; #endif pimd-2.3.2/redhat/000077500000000000000000000000001267035112600137215ustar00rootroot00000000000000pimd-2.3.2/redhat/pimd.init000066400000000000000000000025241267035112600155420ustar00rootroot00000000000000#!/bin/sh # # pimd Starts pimd. # # # chkconfig: 2345 85 40 # description: pimd is a PIM-SM multicast routing daemon # # Source function library . /etc/rc.d/init.d/functions # Get network config . /etc/sysconfig/network # Check that networking is up. if is_yes "${NETWORKING}"; then if [ ! -f /var/lock/subsys/network -a "$1" != stop -a "$1" != status ]; then msg_network_down pimd exit 1 fi else exit 0 fi # Get service config if [ -f /etc/sysconfig/pimd ]; then . /etc/sysconfig/pimd fi RETVAL=0 # See how we were called. case "$1" in start) # Check if the service is already running? if [ ! -f /var/lock/subsys/pimd ]; then show "Starting PIM-SM multicast routing daemon" daemon pimd RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/subsys/pimd else msg_already_running pimd fi ;; stop) if [ -f /var/lock/subsys/pimd ]; then show "Stopping PIM-SM multicast routing daemon" killproc pimd rm -f /var/lock/subsys/pimd else msg_not_running pimd fi ;; status) status pimd exit $? ;; reload|force-reload) if [ -f /var/lock/subsys/pimd ]; then show "Reloading PIM-SM multicast routing daemon" killproc pimd -HUP RETVAL=$? else msg_not_running pimd >&2 exit 7 fi ;; restart) $0 stop $0 start exit $? ;; *) msg_usage "$0 {start|stop|restart|reload|force-reload|status}" exit 3 esac exit $RETVAL pimd-2.3.2/redhat/pimd.spec000066400000000000000000000034701267035112600155320ustar00rootroot00000000000000Name: pimd Version: 2.1.8 Release: 2%{?dist} Summary: pimd, the PIM-SM v2 multicast daemon Group: System Environment/Daemons License: BSD URL: https://github.com/troglobit/%{name}/archive/master.zip Source0: %{name}-master.zip Source1: %{name}.init BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} BuildRequires: make gcc %description This is pimd, a lightweight, stand-alone implementation of Protocol Independent Multicast-Sparse Mode that may be freely distributed and/or deployed under the BSD license. The project implements the full PIM-SM specification according to RFC 2362 with a few noted exceptions (see the RELEASE.NOTES for details). %prep %setup -q -n %{name}-master %build ./configure make %{?_smp_mflags} %install %{__mkdir_p} %{buildroot}%{_sysconfdir} %{__install} -m 0644 pimd.conf %{buildroot}%{_sysconfdir}/ %{__mkdir_p} %{buildroot}%{_initrddir} %{__install} -m 0755 %{SOURCE1} %{buildroot}%{_initrddir}/%{name} %{__mkdir_p} %{buildroot}%{_sbindir} %{__install} -m 0755 %{name} %{buildroot}%{_sbindir}/%{name} %{__mkdir_p} %{buildroot}%{_mandir}/man8 %{__install} -m 0644 pimd.8 %{buildroot}%{_mandir}/man8/pimd.8 %clean %{__rm} -rf %{buildroot} %files %defattr(-,root,root,-) %doc AUTHORS CREDITS FAQ INSTALL LICENSE LICENSE.mrouted README README.config README.config.jp README.debug RELEASE.NOTES TODO %config %{_sysconfdir}/pimd.conf %{_initrddir}/%{name} %{_sbindir}/%{name} %{_mandir}/man8/pimd.8.gz %post chkconfig --add %{name} %changelog * Mon Jan 27 2014 timeos@zssos.sk - 2.1.8 fix for - pimd segfaults in igmp_read() (https://github.com/troglobit/pimd/issues/29) * Thu Dec 31 2013 timeos@zssos.sk - 2.1.8 Initial - Built from upstream version. pimd-2.3.2/route.c000066400000000000000000000761661267035112600137740ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: route.c,v 1.39 2003/02/12 21:56:55 pavlin Exp $ */ #include "defs.h" /* Marian Stagarescu : 07/31/01: * * Administrative scoped multicast filtering im PIMD. This allows an * interface to be configured as an administrative boundary for the * specified scoped address. Packets belonging to the scoped address will * not be forwarded. * * Please note the in order to minimize the search for the matching groups * the implementation is limited to: * * Packets are stopped from being forwarded by installing a NULL outgoing * interface; the user space (pimd) is not up-call-ed any more for * these packets which are dropped by kernel (nil oif) except for * when we de-install the route are re-create it (timer 3 minute). * uses the VIF acl that was installed via config scoped statements. * * this is not an all-purpose packet filtering mechanism. * we tried here to achieve the filtering with minimal processing * (inspect (g) when we are about to install a route for it). * * to use it edit pimd.conf and compile with -DSCOPED_ACL */ static void process_cache_miss (struct igmpmsg *igmpctl); static void process_wrong_iif (struct igmpmsg *igmpctl); static void process_whole_pkt (char *buf); #ifdef SCOPED_ACL /* from mrouted. Contributed by Marian Stagarescu */ static int scoped_addr(vifi_t vifi, uint32_t addr) { struct vif_acl *acl; for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next) { if ((addr & acl->acl_mask) == acl->acl_addr) return 1; } return 0; } /* Contributed by Marian Stagarescu * adapted from mrouted: check for scoped multicast addresses * install null oif if matched */ #define APPLY_SCOPE(g, mp) { \ vifi_t i; \ for (i = 0; i < numvifs; i++) \ if (scoped_addr(i, g)) \ (mp)->oifs = 0; \ } #endif /* SCOPED_ACL */ /* Return the iif for given address */ vifi_t get_iif(uint32_t address) { struct rpfctl rpfc; k_req_incoming(address, &rpfc); if (rpfc.rpfneighbor.s_addr == INADDR_ANY_N) return NO_VIF; return rpfc.iif; } /* Return the PIM neighbor toward a source */ /* If route not found or if a local source or if a directly connected source, * but is not PIM router, or if the first hop router is not a PIM router, * then return NULL. */ pim_nbr_entry_t *find_pim_nbr(uint32_t source) { struct rpfctl rpfc; pim_nbr_entry_t *nbr; uint32_t addr; if (local_address(source) != NO_VIF) return NULL; k_req_incoming(source, &rpfc); if ((rpfc.rpfneighbor.s_addr == INADDR_ANY_N) || (rpfc.iif == NO_VIF)) return NULL; /* Figure out the nexthop neighbor by checking the reverse path */ addr = rpfc.rpfneighbor.s_addr; for (nbr = uvifs[rpfc.iif].uv_pim_neighbors; nbr; nbr = nbr->next) if (nbr->address == addr) return nbr; return NULL; } /* TODO: check again the exact setup if the source is local or directly * connected!!! */ /* TODO: XXX: change the metric and preference for all (S,G) entries per * source or RP? */ /* TODO - If possible, this would be the place to correct set the * source's preference and metric to that obtained from the kernel * and/or unicast routing protocol. For now, set it to the configured * default for local pref/metric. */ /* * Set the iif, upstream router, preference and metric for the route * toward the source. Return TRUE is the route was found, othewise FALSE. * If type==PIM_IIF_SOURCE and if the source is directly connected * then the "upstream" is set to NULL. If srcentry==PIM_IIF_RP, then * "upstream" in case of directly connected "source" will be that "source" * (if it is also PIM router)., */ int set_incoming(srcentry_t *src, int type) { struct rpfctl rpfc; uint32_t src_addr = src->address; uint32_t nbr_addr; struct uvif *vif; pim_nbr_entry_t *nbr; /* Preference will be 0 if directly connected */ src->metric = 0; src->preference = 0; /* The source is a local address */ src->incoming = local_address(src_addr); if (src->incoming != NO_VIF) { /* iif of (*,G) at RP has to be register_if */ if (type == PIM_IIF_RP) src->incoming = reg_vif_num; /* TODO: set the upstream to myself? */ src->upstream = NULL; return TRUE; } src->incoming = find_vif_direct(src_addr); if (src->incoming != NO_VIF) { /* The source is directly connected. Check whether we are * looking for real source or RP */ if (type == PIM_IIF_SOURCE) { src->upstream = NULL; return TRUE; } /* PIM_IIF_RP */ nbr_addr = src_addr; } else { /* TODO: probably need to check the case if the iif is disabled */ /* Use the lastest resource: the kernel unicast routing table */ k_req_incoming(src_addr, &rpfc); if ((rpfc.iif == NO_VIF) || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) { /* couldn't find a route */ if (!IN_LINK_LOCAL_RANGE(src_addr)) { IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF) logit(LOG_DEBUG, 0, "NO ROUTE found for %s", inet_fmt(src_addr, s1, sizeof(s1))); } return FALSE; } src->incoming = rpfc.iif; nbr_addr = rpfc.rpfneighbor.s_addr; /* set the preference for sources that aren't directly connected. */ vif = &uvifs[src->incoming]; src->preference = vif->uv_local_pref; src->metric = vif->uv_local_metric; } /* The upstream router must be a (PIM router) neighbor, otherwise we * are in big trouble ;-) */ vif = &uvifs[src->incoming]; for (nbr = vif->uv_pim_neighbors; nbr; nbr = nbr->next) { if (ntohl(nbr_addr) < ntohl(nbr->address)) continue; if (nbr_addr == nbr->address) { /* The upstream router is found in the list of neighbors. * We are safe! */ src->upstream = nbr; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "For src %s, iif is %d, next hop router is %s", inet_fmt(src_addr, s1, sizeof(s1)), src->incoming, inet_fmt(nbr_addr, s2, sizeof(s2))); return TRUE; } break; } /* TODO: control the number of messages! */ logit(LOG_INFO, 0, "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER", inet_fmt(src_addr, s1, sizeof(s1)), src->incoming, inet_fmt(nbr_addr, s2, sizeof(s2))); src->upstream = NULL; return FALSE; } /* * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where * we have source-specific Join/Prune. */ void add_leaf(vifi_t vifi, uint32_t source, uint32_t group) { mrtentry_t *mrt; mrtentry_t *srcs; vifbitmap_t old_oifs; vifbitmap_t new_oifs; vifbitmap_t new_leaves; /* Don't create routing entries for the LAN scoped addresses */ if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) { /* group <= 224.0.0.255? */ IF_DEBUG(DEBUG_IGMP) logit(LOG_DEBUG, 0, "Not creating routing entry for LAN scoped group %s", inet_fmt(group, s1, sizeof(s1))); return; } /* * XXX: only if I am a DR, the IGMP Join should result in creating * a PIM MRT state. * XXX: Each router must know if it has local members, i.e., whether * it is a last-hop router as well. This info is needed so it will * know whether is allowed to initiate a SPT switch by sending * a PIM (S,G) Join to the high datarate source. * However, if a non-DR last-hop router has not received * a PIM Join, it should not create a PIM state, otherwise later * this state may incorrectly trigger PIM joins. * There is a design flow in pimd, so without making major changes * the best we can do is that the non-DR last-hop router will * record the local members only after it receives PIM Join from the DR * (i.e. after the second or third IGMP Join by the local member). * The downside is that a last-hop router may delay the initiation * of the SPT switch. Sigh... */ if (IN_PIM_SSM_RANGE(group)) mrt = find_route(source, group, MRTF_SG, CREATE); else if (uvifs[vifi].uv_flags & VIFF_DR) mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); else mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrt) return; IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi, inet_fmt(group, s1, sizeof(s1))); if (VIFM_ISSET(vifi, mrt->leaves)) return; /* Already a leaf */ calc_oifs(mrt, &old_oifs); VIFM_COPY(mrt->leaves, new_leaves); VIFM_SET(vifi, new_leaves); /* Add the leaf */ change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, new_leaves, mrt->asserted_oifs, 0); calc_oifs(mrt, &new_oifs); /* Only if I am the DR for that subnet, eventually initiate a Join */ if (!(uvifs[vifi].uv_flags & VIFF_DR)) return; if ((mrt->flags & MRTF_NEW) || (VIFM_ISEMPTY(old_oifs) && (!VIFM_ISEMPTY(new_oifs)))) { /* A new created entry or the oifs have changed * from NULL to non-NULL. */ mrt->flags &= ~MRTF_NEW; FIRE_TIMER(mrt->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call the function below? send_pim_join_prune(mrt->upstream->vifi, mrt->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } /* Check all (S,G) entries and set the inherited "leaf" flag. * TODO: XXX: This won't work for IGMPv3, because there we don't know * whether the (S,G) leaf oif was inherited from the (*,G) entry or * was created by source specific IGMP join. */ for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) { VIFM_COPY(srcs->leaves, new_leaves); VIFM_SET(vifi, new_leaves); change_interfaces(srcs, srcs->incoming, srcs->joined_oifs, srcs->pruned_oifs, new_leaves, srcs->asserted_oifs, 0); } } /* * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where * we have source-specific joins/prunes. */ void delete_leaf(vifi_t vifi, uint32_t source, uint32_t group) { mrtentry_t *mrt; mrtentry_t *srcs; vifbitmap_t new_oifs; vifbitmap_t old_oifs; vifbitmap_t new_leaves; if (IN_PIM_SSM_RANGE(group)) mrt = find_route(source, group, MRTF_SG, DONT_CREATE); else mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrt) return; if (!VIFM_ISSET(vifi, mrt->leaves)) return; /* This interface wasn't leaf */ IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Deleting vif %d for group %s", vifi, inet_fmt(group, s1, sizeof(s1))); calc_oifs(mrt, &old_oifs); /* For SSM, source must match */ if (!IN_PIM_SSM_RANGE(group) || (mrt->source->address==source)) { VIFM_COPY(mrt->leaves, new_leaves); VIFM_CLR(vifi, new_leaves); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, new_leaves, mrt->asserted_oifs, 0); } calc_oifs(mrt, &new_oifs); if ((!VIFM_ISEMPTY(old_oifs)) && VIFM_ISEMPTY(new_oifs)) { /* The result oifs have changed from non-NULL to NULL */ FIRE_TIMER(mrt->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call the function below? send_pim_join_prune(mrt->upstream->vifi, mrt->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } /* Check all (S,G) entries and clear the inherited "leaf" flag. * TODO: XXX: This won't work for IGMPv3, because there we don't know * whether the (S,G) leaf oif was inherited from the (*,G) entry or * was created by source specific IGMP join. */ for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) { VIFM_COPY(srcs->leaves, new_leaves); VIFM_CLR(vifi, new_leaves); change_interfaces(srcs, srcs->incoming, srcs->joined_oifs, srcs->pruned_oifs, new_leaves, srcs->asserted_oifs, 0); } } void calc_oifs(mrtentry_t *mrt, vifbitmap_t *oifs_ptr) { vifbitmap_t oifs; mrtentry_t *grp; mrtentry_t *mrp; /* * oifs = * (((copied_outgoing + my_join) - my_prune) + my_leaves) * - my_asserted_oifs - incoming_interface, * i.e. `leaves` have higher priority than `prunes`, but lower priority * than `asserted`. The incoming interface is always deleted from the oifs */ if (!mrt) { VIFM_CLRALL(*oifs_ptr); return; } VIFM_CLRALL(oifs); if (!(mrt->flags & MRTF_PMBR)) { /* Either (*,G) or (S,G). Merge with the oifs from the (*,*,RP) */ mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; if (mrp) { VIFM_MERGE(oifs, mrp->joined_oifs, oifs); VIFM_CLR_MASK(oifs, mrp->pruned_oifs); VIFM_MERGE(oifs, mrp->leaves, oifs); VIFM_CLR_MASK(oifs, mrp->asserted_oifs); } } if (mrt->flags & MRTF_SG) { /* (S,G) entry. Merge with the oifs from (*,G) */ grp = mrt->group->grp_route; if (grp) { VIFM_MERGE(oifs, grp->joined_oifs, oifs); VIFM_CLR_MASK(oifs, grp->pruned_oifs); VIFM_MERGE(oifs, grp->leaves, oifs); VIFM_CLR_MASK(oifs, grp->asserted_oifs); } } /* Calculate my own stuff */ VIFM_MERGE(oifs, mrt->joined_oifs, oifs); VIFM_CLR_MASK(oifs, mrt->pruned_oifs); VIFM_MERGE(oifs, mrt->leaves, oifs); VIFM_CLR_MASK(oifs, mrt->asserted_oifs); VIFM_COPY(oifs, *oifs_ptr); } /* * Set the iif, join/prune/leaves/asserted interfaces. Calculate and * set the oifs. * Return 1 if oifs change from NULL to not-NULL. * Return -1 if oifs change from non-NULL to NULL * else return 0 * If the iif change or if the oifs change from NULL to non-NULL * or vice-versa, then schedule that mrtentry join/prune timer to * timeout immediately. */ int change_interfaces(mrtentry_t *mrt, vifi_t new_iif, vifbitmap_t new_joined_oifs_, vifbitmap_t new_pruned_oifs, vifbitmap_t new_leaves_, vifbitmap_t new_asserted_oifs, uint16_t flags) { vifbitmap_t new_joined_oifs; /* The oifs for that particular mrtentry */ vifbitmap_t old_joined_oifs __attribute__ ((unused)); vifbitmap_t old_pruned_oifs __attribute__ ((unused)); vifbitmap_t old_leaves __attribute__ ((unused)); vifbitmap_t new_leaves; vifbitmap_t old_asserted_oifs __attribute__ ((unused)); vifbitmap_t new_real_oifs; /* The result oifs */ vifbitmap_t old_real_oifs; vifi_t old_iif; rpentry_t *rp; cand_rp_t *cand_rp; kernel_cache_t *kc; rp_grp_entry_t *rp_grp; grpentry_t *grp; mrtentry_t *srcs; mrtentry_t *mwc; mrtentry_t *mrp; int delete_mrt_flag; int result; int fire_timer_flag; if (!mrt) return 0; VIFM_COPY(new_joined_oifs_, new_joined_oifs); VIFM_COPY(new_leaves_, new_leaves); old_iif = mrt->incoming; VIFM_COPY(mrt->joined_oifs, old_joined_oifs); VIFM_COPY(mrt->leaves, old_leaves); VIFM_COPY(mrt->pruned_oifs, old_pruned_oifs); VIFM_COPY(mrt->asserted_oifs, old_asserted_oifs); VIFM_COPY(mrt->oifs, old_real_oifs); mrt->incoming = new_iif; VIFM_COPY(new_joined_oifs, mrt->joined_oifs); VIFM_COPY(new_pruned_oifs, mrt->pruned_oifs); VIFM_COPY(new_leaves, mrt->leaves); VIFM_COPY(new_asserted_oifs, mrt->asserted_oifs); calc_oifs(mrt, &new_real_oifs); if (VIFM_ISEMPTY(old_real_oifs)) { if (VIFM_ISEMPTY(new_real_oifs)) result = 0; else result = 1; } else { if (VIFM_ISEMPTY(new_real_oifs)) result = -1; else result = 0; } if ((VIFM_SAME(new_real_oifs, old_real_oifs)) && (new_iif == old_iif) && !(flags & MFC_UPDATE_FORCE)) return 0; /* Nothing to change */ if ((result != 0) || (new_iif != old_iif) || (flags & MFC_UPDATE_FORCE)) { FIRE_TIMER(mrt->jp_timer); } VIFM_COPY(new_real_oifs, mrt->oifs); if (mrt->flags & MRTF_PMBR) { /* (*,*,RP) entry */ rp = mrt->source; if (!rp) return 0; /* Shouldn't happen */ rp->incoming = new_iif; cand_rp = rp->cand_rp; if (VIFM_ISEMPTY(new_real_oifs)) { delete_mrt_flag = TRUE; } else { delete_mrt_flag = FALSE; #ifdef RSRR rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK); #endif /* RSRR */ } if (mrt->flags & MRTF_KERNEL_CACHE) { /* Update the kernel MFC entries */ if (delete_mrt_flag == TRUE) { /* XXX: no need to send RSRR message. Will do it when * delete the mrtentry. */ for (kc = mrt->kernel_cache; kc; kc = kc->next) delete_mrtentry_all_kernel_cache(mrt); } else { /* here mrt->source->address is the RP address */ for (kc = mrt->kernel_cache; kc; kc = kc->next) k_chg_mfc(igmp_socket, kc->source, kc->group, new_iif, new_real_oifs, mrt->source->address); } } /* * Update all (*,G) entries associated with this RP. * The particular (*,G) outgoing are not changed, but the change * in the (*,*,RP) oifs may have affect the real oifs. */ fire_timer_flag = FALSE; for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp->rpnext) { if (grp->grp_route) { if (change_interfaces(grp->grp_route, new_iif, grp->grp_route->joined_oifs, grp->grp_route->pruned_oifs, grp->grp_route->leaves, grp->grp_route->asserted_oifs, flags)) fire_timer_flag = TRUE; } else { /* Change all (S,G) entries if no (*,G) */ for (srcs = grp->mrtlink; srcs; srcs = srcs->grpnext) { if (srcs->flags & MRTF_RP) { if (change_interfaces(srcs, new_iif, srcs->joined_oifs, srcs->pruned_oifs, srcs->leaves, srcs->asserted_oifs, flags)) fire_timer_flag = TRUE; } else { if (change_interfaces(srcs, srcs->incoming, srcs->joined_oifs, srcs->pruned_oifs, srcs->leaves, srcs->asserted_oifs, flags)) fire_timer_flag = TRUE; } } } } } if (fire_timer_flag == TRUE) FIRE_TIMER(mrt->jp_timer); if (delete_mrt_flag == TRUE) { /* TODO: XXX: trigger a Prune message? Don't delete now, it will * be automatically timed out. If want to delete now, don't * reference to it anymore! delete_mrtentry(mrt); */ } return result; /* (*,*,RP) */ } if (mrt->flags & MRTF_WC) { /* (*,G) entry */ if (VIFM_ISEMPTY(new_real_oifs)) { delete_mrt_flag = TRUE; } else { delete_mrt_flag = FALSE; #ifdef RSRR rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK); #endif /* RSRR */ } if (mrt->flags & MRTF_KERNEL_CACHE) { if (delete_mrt_flag == TRUE) { delete_mrtentry_all_kernel_cache(mrt); } else { for (kc = mrt->kernel_cache; kc; kc = kc->next) k_chg_mfc(igmp_socket, kc->source, kc->group, new_iif, new_real_oifs, mrt->group->rpaddr); } } /* Update all (S,G) entries for this group. * For the (S,G)RPbit entries the iif is the iif toward the RP; * The particular (S,G) oifs are not changed, but the change in the * (*,G) oifs may affect the real oifs. */ fire_timer_flag = FALSE; for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) { if (srcs->flags & MRTF_RP) { if (change_interfaces(srcs, new_iif, srcs->joined_oifs, srcs->pruned_oifs, srcs->leaves, srcs->asserted_oifs, flags)) fire_timer_flag = TRUE; } else { if (change_interfaces(srcs, srcs->incoming, srcs->joined_oifs, srcs->pruned_oifs, srcs->leaves, srcs->asserted_oifs, flags)) fire_timer_flag = TRUE; } } if (fire_timer_flag == TRUE) FIRE_TIMER(mrt->jp_timer); if (delete_mrt_flag == TRUE) { /* TODO: XXX: the oifs are NULL. Send a Prune message? */ } return result; /* (*,G) */ } /* (S,G) entry */ if (mrt->flags & MRTF_SG) { mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; mwc = mrt->group->grp_route; #ifdef KERNEL_MFC_WC_G mrtentry_t *tmp; /* Check whether (*,*,RP) or (*,G) have different (iif,oifs) from * the (S,G). If "yes", then forbid creating (*,G) MFC. */ for (tmp = mrp; 1; tmp = mwc) { while (1) { vifbitmap_t oifs; if (!tmp) break; if (tmp->flags & MRTF_MFC_CLONE_SG) break; if (tmp->incoming != mrt->incoming) { delete_single_kernel_cache_addr(tmp, INADDR_ANY_N, mrt->group->group); tmp->flags |= MRTF_MFC_CLONE_SG; break; } calc_oifs(tmp, &oifs); if (!(VIFM_SAME(new_real_oifs, oifs))) tmp->flags |= MRTF_MFC_CLONE_SG; break; } if (tmp == mwc) break; } #endif /* KERNEL_MFC_WC_G */ if (VIFM_ISEMPTY(new_real_oifs)) { delete_mrt_flag = TRUE; } else { delete_mrt_flag = FALSE; #ifdef RSRR rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK); #endif } if (mrt->flags & MRTF_KERNEL_CACHE) { if (delete_mrt_flag == TRUE) delete_mrtentry_all_kernel_cache(mrt); else k_chg_mfc(igmp_socket, mrt->source->address, mrt->group->group, new_iif, new_real_oifs, mrt->group->rpaddr); } if (old_iif != new_iif) { if (new_iif == mrt->source->incoming) { /* For example, if this was (S,G)RPbit with iif toward the RP, * and now switch to the Shortest Path. * The setup of MRTF_SPT flag must be * done by the external calling function (triggered only * by receiving of a data from the source.) */ mrt->flags &= ~MRTF_RP; /* TODO: XXX: delete? Check again where will be the best * place to set it. mrt->flags |= MRTF_SPT; */ } if ((mwc && mwc->incoming == new_iif) || (mrp && mrp->incoming == new_iif)) { /* If the new iif points toward the RP, reset the SPT flag. * (PIM-SM-spec-10.ps pp. 11, 2.10, last sentence of first * paragraph. */ /* TODO: XXX: check again! */ mrt->flags &= ~MRTF_SPT; mrt->flags |= MRTF_RP; } } /* TODO: XXX: if this is (S,G)RPbit entry and the oifs==(*,G)oifs, * then delete the (S,G) entry?? The same if we have (*,*,RP) ? */ if (delete_mrt_flag == TRUE) { /* TODO: XXX: the oifs are NULL. Send a Prune message ? */ } /* TODO: XXX: have the feeling something is missing.... */ return result; /* (S,G) */ } return result; } /* TODO: implement it. Required to allow changing of the physical interfaces * configuration without need to restart pimd. */ int delete_vif_from_mrt(vifi_t vifi __attribute__((unused))) { return TRUE; } void process_kernel_call(void) { struct igmpmsg *igmpctl = (struct igmpmsg *)igmp_recv_buf; switch (igmpctl->im_msgtype) { case IGMPMSG_NOCACHE: process_cache_miss(igmpctl); break; case IGMPMSG_WRONGVIF: process_wrong_iif(igmpctl); break; case IGMPMSG_WHOLEPKT: process_whole_pkt(igmp_recv_buf); break; default: IF_DEBUG(DEBUG_KERN) logit(LOG_DEBUG, 0, "Unknown IGMP message type from kernel: %d", igmpctl->im_msgtype); break; } } /* * TODO: when cache miss, check the iif, because probably ASSERTS * shoult take place */ static void process_cache_miss(struct igmpmsg *igmpctl) { uint32_t source, mfc_source; uint32_t group; uint32_t rp_addr; vifi_t iif; mrtentry_t *mrt; mrtentry_t *mrp; /* When there is a cache miss, we check only the header of the packet * (and only it should be sent up by the kernel. */ group = igmpctl->im_dst.s_addr; source = mfc_source = igmpctl->im_src.s_addr; iif = igmpctl->im_vif; IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Cache miss, src %s, dst %s, iif %d", inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), iif); /* TODO: XXX: check whether the kernel generates cache miss for the LAN scoped addresses */ if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) return; /* Don't create routing entries for the LAN scoped addresses */ /* TODO: check if correct in case the source is one of my addresses */ /* If I am the DR for this source, create (S,G) and add the register_vif * to the oifs. */ if ((uvifs[iif].uv_flags & VIFF_DR) && (find_vif_direct_local(source) == iif)) { mrt = find_route(source, group, MRTF_SG, CREATE); if (!mrt) return; mrt->flags &= ~MRTF_NEW; /* set reg_vif_num as outgoing interface ONLY if I am not the RP */ if (mrt->group->rpaddr != my_cand_rp_address) VIFM_SET(reg_vif_num, mrt->joined_oifs); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } else { mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); switch_shortest_path(source, group); if (!mrt) return; } /* TODO: if there are too many cache miss for the same (S,G), * install negative cache entry in the kernel (oif==NULL) to prevent * too many upcalls. */ if (mrt->incoming == iif) { if (!VIFM_ISEMPTY(mrt->oifs)) { if (mrt->flags & MRTF_SG) { /* TODO: check that the RPbit is not set? */ /* TODO: XXX: TIMER implem. dependency! */ if (mrt->timer < PIM_DATA_TIMEOUT) SET_TIMER(mrt->timer, PIM_DATA_TIMEOUT); if (!(mrt->flags & MRTF_SPT)) { mrp = mrt->group->grp_route; if (!mrp) mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; if (mrp) { /* Check if the (S,G) iif is different from * the (*,G) or (*,*,RP) iif */ if ((mrt->incoming != mrp->incoming) || (mrt->upstream != mrp->upstream)) { mrt->flags |= MRTF_SPT; mrt->flags &= ~MRTF_RP; } } } } if (mrt->flags & MRTF_PMBR) rp_addr = mrt->source->address; else rp_addr = mrt->group->rpaddr; mfc_source = source; #ifdef KERNEL_MFC_WC_G if (mrt->flags & (MRTF_WC | MRTF_PMBR)) if (!(mrt->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrt, mfc_source, group, MFC_MOVE_FORCE); #ifdef SCOPED_ACL APPLY_SCOPE(group, mrt); #endif k_chg_mfc(igmp_socket, mfc_source, group, iif, mrt->oifs, rp_addr); /* No need for RSRR message, because nothing has changed. */ } return; /* iif match */ } /* The iif doesn't match */ if (mrt->flags & MRTF_SG) { /* Arrived on wrong interface */ if (mrt->flags & MRTF_SPT) return; mrp = mrt->group->grp_route; if (!mrp) mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink; if (mrp) { /* Forward on (*,G) or (*,*,RP) */ if (mrp->incoming == iif) { #ifdef KERNEL_MFC_WC_G if (!(mrp->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrp, mfc_source, group, 0); #ifdef SCOPED_ACL /* marian: not sure if we reach here with our scoped traffic? */ APPLY_SCOPE(group, mrt); #endif k_chg_mfc(igmp_socket, mfc_source, group, iif, mrp->oifs, mrt->group->rpaddr); #ifdef RSRR rsrr_cache_send(mrp, RSRR_NOTIFICATION_OK); #endif /* RSRR */ } } } } /* * A multicast packet has been received on wrong iif by the kernel. * Check for a matching entry. If there is (S,G) with reset SPTbit and * the packet was received on the iif toward the source, this completes * the switch to the shortest path and triggers (S,G) prune toward the RP * (unless I am the RP). * Otherwise, if the packet's iif is in the oiflist of the routing entry, * trigger an Assert. */ static void process_wrong_iif(struct igmpmsg *igmpctl) { uint32_t source; uint32_t group; vifi_t iif; mrtentry_t *mrt; group = igmpctl->im_dst.s_addr; source = igmpctl->im_src.s_addr; iif = igmpctl->im_vif; IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Wrong iif: src %s, dst %s, iif %d", inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), iif); /* Don't create routing entries for the LAN scoped addresses */ if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) return; /* Ignore if it comes on register vif. register vif is neither SPT iif, * neither is used to send asserts out. */ if (uvifs[iif].uv_flags & VIFF_REGISTER) return; mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (!mrt) return; /* * TODO: check again! */ if (mrt->flags & MRTF_SG) { if (!(mrt->flags & MRTF_SPT)) { if (mrt->source->incoming == iif) { /* Switch to the Shortest Path */ mrt->flags |= MRTF_SPT; mrt->flags &= ~MRTF_RP; add_kernel_cache(mrt, source, group, MFC_MOVE_FORCE); k_chg_mfc(igmp_socket, source, group, iif, mrt->oifs, mrt->group->rpaddr); FIRE_TIMER(mrt->jp_timer); #ifdef RSRR rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK); #endif /* RSRR */ return; } } } /* Trigger an Assert */ if (VIFM_ISSET(iif, mrt->oifs)) send_pim_assert(source, group, iif, mrt); } /* * Receives whole packets from the register vif entries * in the kernel, and calls the send_pim_register procedure to * encapsulate the packets and unicasts them to the RP. */ static void process_whole_pkt(char *buf) { send_pim_register((char *)(buf + sizeof(struct igmpmsg))); } mrtentry_t *switch_shortest_path(uint32_t source, uint32_t group) { mrtentry_t *mrt; IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Switch shortest path (SPT): src %s, group %s", inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2))); /* TODO: XXX: prepare and send immediately the (S,G) join? */ mrt = find_route(source, group, MRTF_SG, CREATE); if (mrt) { if (mrt->flags & MRTF_NEW) { mrt->flags &= ~MRTF_NEW; } else if (mrt->flags & MRTF_RP || IN_PIM_SSM_RANGE(group)) { /* (S,G)RPbit with iif toward RP. Reset to (S,G) with iif * toward S. Delete the kernel cache (if any), because * change_interfaces() will reset it with iif toward S * and no data will arrive from RP before the switch * really occurs. * For SSM, (S,G)RPbit entry does not exist but switch to * SPT must be allowed right away. */ mrt->flags &= ~MRTF_RP; mrt->incoming = mrt->source->incoming; mrt->upstream = mrt->source->upstream; delete_mrtentry_all_kernel_cache(mrt); change_interfaces(mrt, mrt->incoming, mrt->joined_oifs, mrt->pruned_oifs, mrt->leaves, mrt->asserted_oifs, 0); } SET_TIMER(mrt->timer, PIM_DATA_TIMEOUT); FIRE_TIMER(mrt->jp_timer); } return mrt; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/routesock.c000066400000000000000000000246661267035112600146520ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include #include #include "defs.h" #include #include #ifdef HAVE_ROUTING_SOCKETS #include #endif #include #include #include /* All the BSDs have routing sockets (not Netlink), but only Linux seems * to have SIOCGETRPF, which is used in the #else below ... the original * authors wanted to merge routesock.c and netlink.c, but I don't know * anymore. --Joachim */ #ifdef HAVE_ROUTING_SOCKETS union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_dl sdl; } so_dst, so_ifp; typedef union sockunion *sup; int routing_socket = -1; int rtm_addrs; static pid_t pid; struct rt_metrics rt_metrics; uint32_t rtm_inits; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; /* * Local functions definitions. */ static int getmsg(struct rt_msghdr *, int, struct rpfctl *rpfinfo); /* * TODO: check again! */ #ifdef IRIX #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ : sizeof(__uint64_t)) #else #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ : sizeof(long)) #endif /* IRIX */ #ifdef HAVE_SA_LEN #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) #else #define ADVANCE(x, n) (x += ROUNDUP(sizeof(*(n)))) /* XXX: sizeof(struct sockaddr) */ #endif /* Open and initialize the routing socket */ int init_routesock(void) { #if 0 int on = 0; #endif routing_socket = socket(PF_ROUTE, SOCK_RAW, 0); if (routing_socket < 0) { logit(LOG_ERR, errno, "Failed creating routing socket"); return -1; } if (fcntl(routing_socket, F_SETFL, O_NONBLOCK) == -1) { logit(LOG_ERR, errno, "Failed setting routing socket as non-blocking"); return -1; } #if 0 /* XXX: if it is OFF, no queries will succeed (!?) */ if (setsockopt(routing_socket, SOL_SOCKET, SO_USELOOPBACK, (char *)&on, sizeof(on)) < 0) { logit(LOG_ERR, errno , "setsockopt(SO_USELOOPBACK, 0)"); return -1; } #endif return 0; } /* get the rpf neighbor info */ int k_req_incoming(uint32_t source, struct rpfctl *rpfp) { int rlen, l, flags = RTF_STATIC, retry_count = 3; sup su; static int seq; char *cp = m_rtmsg.m_space; struct rpfctl rpfinfo; /* TODO: a hack!!!! */ #ifdef HAVE_SA_LEN #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ l = ROUNDUP(u.sa.sa_len); \ memcpy(cp, &(u), l); \ cp += l; \ } #else #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ l = ROUNDUP(sizeof(struct sockaddr)); \ memcpy(cp, &(u), l); \ cp += l; \ } #endif /* HAVE_SA_LEN */ /* initialize */ rpfp->rpfneighbor.s_addr = INADDR_ANY_N; rpfp->source.s_addr = source; /* check if local address or directly connected before calling the * routing socket */ if ((rpfp->iif = find_vif_direct_local(source)) != NO_VIF) { rpfp->rpfneighbor.s_addr = source; return TRUE; } /* prepare the routing socket params */ rtm_addrs |= RTA_DST; rtm_addrs |= RTA_IFP; su = &so_dst; su->sin.sin_family = AF_INET; #ifdef HAVE_SA_LEN su->sin.sin_len = sizeof(struct sockaddr_in); #endif su->sin.sin_addr.s_addr = source; if (inet_lnaof(su->sin.sin_addr) == INADDR_ANY) { IF_DEBUG(DEBUG_RPF) { logit(LOG_DEBUG, 0, "k_req_incoming: Invalid source %s", inet_fmt(source, s1, sizeof(s1))); } return FALSE; } so_ifp.sa.sa_family = AF_LINK; #ifdef HAVE_SA_LEN so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); #endif flags |= RTF_UP; flags |= RTF_HOST; flags |= RTF_GATEWAY; errno = 0; memset (&m_rtmsg, 0, sizeof(m_rtmsg)); #define rtm m_rtmsg.m_rtm rtm.rtm_type = RTM_GET; rtm.rtm_flags = flags; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = ++seq; rtm.rtm_addrs = rtm_addrs; rtm.rtm_rmx = rt_metrics; rtm.rtm_inits = rtm_inits; NEXTADDR(RTA_DST, so_dst); NEXTADDR(RTA_IFP, so_ifp); rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; rlen = write(routing_socket, &m_rtmsg, l); if (rlen <= 0) { IF_DEBUG(DEBUG_RPF | DEBUG_KERN) { if (errno == ESRCH) logit(LOG_DEBUG, 0, "Writing to routing socket: no such route"); else logit(LOG_DEBUG, 0, "Error writing to routing socket"); } return FALSE; } pid = getpid(); while (1) { rlen = read(routing_socket, &m_rtmsg, sizeof(m_rtmsg)); if (rlen < 0) { if (errno == EINTR) continue; /* Signalled, retry syscall. */ if (errno == EAGAIN && retry_count--) { logit(LOG_DEBUG, 0, "Kernel busy, retrying (%d/3) routing socket read in one sec ...", 3 - retry_count); sleep(1); continue; } IF_DEBUG(DEBUG_RPF | DEBUG_KERN) logit(LOG_DEBUG, errno, "Read from routing socket failed"); return FALSE; } if (rlen > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)) continue; break; } memset(&rpfinfo, 0, sizeof(rpfinfo)); if (getmsg(&rtm, l, &rpfinfo)) { rpfp->rpfneighbor.s_addr = rpfinfo.rpfneighbor.s_addr; rpfp->iif = rpfinfo.iif; } #undef rtm return TRUE; } static void find_sockaddrs(struct rt_msghdr *rtm, struct sockaddr **dst, struct sockaddr **gate, struct sockaddr **mask, struct sockaddr_dl **ifp) { int i; char *cp = (char *)(rtm + 1); struct sockaddr *sa; if (rtm->rtm_addrs) { for (i = 1; i; i <<= 1) { if (i & rtm->rtm_addrs) { sa = (struct sockaddr *)cp; switch (i) { case RTA_DST: *dst = sa; break; case RTA_GATEWAY: *gate = sa; break; case RTA_NETMASK: *mask = sa; break; case RTA_IFP: if (sa->sa_family == AF_LINK && ((struct sockaddr_dl *)sa)->sdl_nlen) *ifp = (struct sockaddr_dl *)sa; break; } ADVANCE(cp, sa); } } } } /* * Returns TRUE on success, FALSE otherwise. rpfinfo contains the result. */ static int getmsg(struct rt_msghdr *rtm, int msglen __attribute__((unused)), struct rpfctl *rpf) { struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; struct sockaddr_dl *ifp = NULL; struct in_addr in; vifi_t vifi; struct uvif *v; if (!rpf) { logit(LOG_WARNING, 0, "Missing rpf pointer to routesock.c:getmsg()!"); return FALSE; } rpf->iif = NO_VIF; rpf->rpfneighbor.s_addr = INADDR_ANY; in = ((struct sockaddr_in *)&so_dst)->sin_addr; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "route to: %s", inet_fmt(in.s_addr, s1, sizeof(s1))); find_sockaddrs(rtm, &dst, &gate, &mask, &ifp); if (!ifp) { /* No incoming interface */ IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "No incoming interface for destination %s", inet_fmt(in.s_addr, s1, sizeof(s1))); return FALSE; } if (dst && mask) mask->sa_family = dst->sa_family; if (dst) { in = ((struct sockaddr_in *)dst)->sin_addr; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, " destination is: %s", inet_fmt(in.s_addr, s1, sizeof(s1))); } if (gate && (rtm->rtm_flags & RTF_GATEWAY)) { in = ((struct sockaddr_in *)gate)->sin_addr; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, " gateway is: %s", inet_fmt(in.s_addr, s1, sizeof(s1))); rpf->rpfneighbor = in; } for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* get the number of the interface by matching the name */ if ((strlen(v->uv_name) == ifp->sdl_nlen) && !(strncmp(v->uv_name, ifp->sdl_data, ifp->sdl_nlen))) break; } /* Found inbound interface in vifi */ rpf->iif = vifi; IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, " iif is %d", vifi); if (vifi >= numvifs) { IF_DEBUG(DEBUG_RPF) logit(LOG_DEBUG, 0, "Invalid incoming interface for destination %s, because of invalid virtual interface", inet_fmt(in.s_addr, s1, sizeof(s1))); return FALSE; /* invalid iif */ } return TRUE; } #else /* !HAVE_ROUTING_SOCKETS -- if we want to run on Linux without Netlink */ /* API compat dummy. */ int init_routesock(void) { return dup(udp_socket); } /* * Return in rpfcinfo the incoming interface and the next hop router * toward source. */ /* TODO: check whether next hop router address is in network or host order */ int k_req_incoming(uint32_t source, struct rpfctl *rpfcinfo) { rpfcinfo->source.s_addr = source; rpfcinfo->iif = NO_VIF; /* Initialize, will be changed in kernel */ rpfcinfo->rpfneighbor.s_addr = INADDR_ANY; /* Initialize */ if (ioctl(udp_socket, SIOCGETRPF, (char *) rpfcinfo) < 0) { logit(LOG_WARNING, errno, "Failed ioctl SIOCGETRPF in k_req_incoming()"); return FALSE; } return TRUE; } #endif /* HAVE_ROUTING_SOCKETS */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/rp.c000066400000000000000000000676301267035112600132530ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "defs.h" /* * The hash function. Stollen from Eddy's (eddy@isi.edu) * implementation (for compatibility ;) */ #define SEED1 1103515245 #define SEED2 12345 #define RP_HASH_VALUE(G, M, C) (((SEED1) * (((SEED1) * ((G) & (M)) + (SEED2)) ^ (C)) + (SEED2)) % 0x80000000) cand_rp_t *cand_rp_list; grp_mask_t *grp_mask_list; cand_rp_t *segmented_cand_rp_list; grp_mask_t *segmented_grp_mask_list; uint16_t curr_bsr_fragment_tag; uint8_t curr_bsr_priority; uint32_t curr_bsr_address; uint32_t curr_bsr_hash_mask; uint16_t pim_bootstrap_timer; /* For electing the BSR and * sending Cand-RP-set msgs */ uint8_t my_bsr_priority; uint32_t my_bsr_address; uint32_t my_bsr_hash_mask; uint8_t cand_bsr_flag = FALSE; /* Set to TRUE if I am * a candidate BSR */ uint32_t my_cand_rp_address; uint8_t my_cand_rp_priority; uint16_t my_cand_rp_holdtime; uint16_t my_cand_rp_adv_period; /* The locally configured * Cand-RP adv. period. */ uint16_t pim_cand_rp_adv_timer; uint8_t cand_rp_flag = FALSE; /* Candidate RP flag */ struct cand_rp_adv_message_ cand_rp_adv_message; uint32_t rp_my_ipv4_hashmask; /* * Local functions definition. */ static cand_rp_t *add_cand_rp (cand_rp_t **used_cand_rp_list, uint32_t address); static grp_mask_t *add_grp_mask (grp_mask_t **used_grp_mask_list, uint32_t group_addr, uint32_t group_mask, uint32_t hash_mask); static void delete_grp_mask_entry (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, grp_mask_t *grp_mask_delete); static void delete_rp_entry (cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, cand_rp_t *cand_rp_ptr); void init_rp_and_bsr(void) { /* TODO: if the grplist is not NULL, remap all groups ASAP! */ delete_rp_list(&cand_rp_list, &grp_mask_list); delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list); if (cand_bsr_flag == FALSE) { /* * If I am not candidat BSR, initialize the "current BSR" * as having the lowest priority. */ curr_bsr_fragment_tag = 0; curr_bsr_priority = 0; /* Lowest priority */ curr_bsr_address = INADDR_ANY_N; /* Lowest priority */ MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, curr_bsr_hash_mask); SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT); } else { curr_bsr_fragment_tag = RANDOM(); curr_bsr_priority = my_bsr_priority; curr_bsr_address = my_bsr_address; curr_bsr_hash_mask = my_bsr_hash_mask; SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay()); } if (cand_rp_flag != FALSE) { MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, rp_my_ipv4_hashmask); /* Setup the Cand-RP-Adv-Timer */ SET_TIMER(pim_cand_rp_adv_timer, RANDOM() % my_cand_rp_adv_period); } } uint16_t bootstrap_initial_delay(void) { uint32_t addr_delay; uint32_t delay; uint32_t log_mask; int32_t log_of_2; uint8_t best_priority; /* * The bootstrap timer initial value (if Cand-BSR). * It depends of the bootstrap router priority: * higher priority has shorter value: * * delay = 5 + 2 * log_2(1 + best_priority - myPriority) + addr_delay; * * best_priority = Max(storedPriority, myPriority); * if (best_priority == myPriority) * addr_delay = log_2(bestAddr - myAddr)/16; * else * addr_delay = 2 - (myAddr/2^31); */ best_priority = max(curr_bsr_priority, my_bsr_priority); if (best_priority == my_bsr_priority) { addr_delay = ntohl(curr_bsr_address) - ntohl(my_bsr_address); /* Calculate the integer part of log_2 of (bestAddr - myAddr) */ /* To do so, have to find the position number of the first bit * from left which is `1` */ log_mask = sizeof(addr_delay) << 3; log_mask = (1 << (log_mask - 1)); /* Set the leftmost bit to `1` */ for (log_of_2 = (sizeof(addr_delay) << 3) - 1 ; log_of_2; log_of_2--) { if (addr_delay & log_mask) break; log_mask >>= 1; /* Start shifting `1` on right */ } addr_delay = log_of_2 / 16; } else { addr_delay = 2 - (ntohl(my_bsr_address) / ( 1 << 31)); } delay = 1 + best_priority - my_bsr_priority; /* Calculate log_2(delay) */ log_mask = sizeof(delay) << 3; log_mask = (1 << (log_mask - 1)); /* Set the leftmost bit to `1` */ for (log_of_2 = (sizeof(delay) << 3) - 1 ; log_of_2; log_of_2--) { if (delay & log_mask) break; log_mask >>= 1; /* Start shifting `1` on right */ } delay = 5 + 2 * log_of_2 + addr_delay; return (uint16_t)delay; } static cand_rp_t *add_cand_rp(cand_rp_t **used_cand_rp_list, uint32_t address) { cand_rp_t *prev = NULL; cand_rp_t *next; cand_rp_t *ptr; rpentry_t *entry; uint32_t addr_h = ntohl(address); /* The ordering is the bigger first */ for (next = *used_cand_rp_list; next; prev = next, next = next->next) { if (ntohl(next->rpentry->address) > addr_h) continue; if (next->rpentry->address == address) return next; else break; } /* Create and insert the new entry between prev and next */ ptr = (cand_rp_t *)calloc(1, sizeof(cand_rp_t)); if (!ptr) logit(LOG_ERR, 0, "Ran out of memory in add_cand_rp()"); ptr->rp_grp_next = NULL; ptr->next = next; ptr->prev = prev; if (next) next->prev = ptr; if (prev == NULL) *used_cand_rp_list = ptr; else prev->next = ptr; entry = (rpentry_t *)calloc(1, sizeof(rpentry_t)); if (!entry) logit(LOG_ERR, 0, "Ran out of memory in add_cand_rp()"); ptr->rpentry = entry; entry->next = NULL; entry->prev = NULL; entry->address = address; entry->mrtlink = NULL; entry->incoming = NO_VIF; entry->upstream = NULL; /* TODO: setup the metric and the preference as ~0 (the lowest)? */ entry->metric = ~0; entry->preference = ~0; RESET_TIMER(entry->timer); entry->cand_rp = ptr; /* TODO: XXX: check whether there is a route to that RP: if return value * is FALSE, then no route. */ if (local_address(entry->address) == NO_VIF) /* TODO: check for error and delete */ set_incoming(entry, PIM_IIF_RP); else /* TODO: XXX: CHECK!!! */ entry->incoming = reg_vif_num; return ptr; } static grp_mask_t *add_grp_mask(grp_mask_t **used_grp_mask_list, uint32_t group_addr, uint32_t group_mask, uint32_t hash_mask) { grp_mask_t *prev = NULL; grp_mask_t *next; grp_mask_t *ptr; uint32_t prefix_h = ntohl(group_addr & group_mask); /* The ordering of group_addr is: bigger first */ for (next = *used_grp_mask_list; next; prev = next, next = next->next) { if (ntohl(next->group_addr & next->group_mask) > prefix_h) continue; /* The ordering of group_mask is: bigger (longer) first */ if ((next->group_addr & next->group_mask) == (group_addr & group_mask)) { if (ntohl(next->group_mask) > ntohl(group_mask)) continue; else if (next->group_mask == group_mask) return next; else break; } } ptr = (grp_mask_t *)calloc(1, sizeof(grp_mask_t)); if (!ptr) logit(LOG_ERR, 0, "Ran out of memory in add_grp_mask()"); ptr->grp_rp_next = (rp_grp_entry_t *)NULL; ptr->next = next; ptr->prev = prev; if (next) next->prev = ptr; if (prev == NULL) *used_grp_mask_list = ptr; else prev->next = ptr; ptr->group_addr = group_addr; ptr->group_mask = group_mask; ptr->hash_mask = hash_mask; ptr->group_rp_number = 0; ptr->fragment_tag = 0; return ptr; } /* TODO: XXX: BUG: a remapping for some groups currently using some other * grp_mask may be required by the addition of the new entry!!! * Remapping all groups might be a costly process... */ rp_grp_entry_t *add_rp_grp_entry(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t rp_addr, uint8_t rp_priority, uint16_t rp_holdtime, uint32_t group_addr, uint32_t group_mask, uint32_t bsr_hash_mask, uint16_t fragment_tag) { cand_rp_t *cand_rp_ptr; grp_mask_t *mask_ptr; rpentry_t *rpentry_ptr; rp_grp_entry_t *entry_next; rp_grp_entry_t *entry_new; rp_grp_entry_t *entry_prev = NULL; grpentry_t *grpentry_ptr_prev; grpentry_t *grpentry_ptr_next; uint32_t rp_addr_h; uint8_t old_highest_priority = ~0; /* Smaller value means "higher" */ /* Input data verification */ if (!inet_valid_host(rp_addr)) return NULL; if (!IN_CLASSD(ntohl(group_addr))) return NULL; mask_ptr = add_grp_mask(used_grp_mask_list, group_addr, group_mask, bsr_hash_mask); if (mask_ptr == NULL) return NULL; /* TODO: delete */ #if 0 if (mask_ptr->grp_rp_next) { /* Check for obsolete grp_rp chain */ if ((my_bsr_address != curr_bsr_address) && (mask_ptr->grp_rp_next->fragment_tag != fragment_tag)) { /* This grp_rp chain is obsolete. Delete it. */ delete_grp_mask(used_cand_rp_list, used_grp_mask_list, group_addr, group_mask); mask_ptr = add_grp_mask(used_grp_mask_list, group_addr, group_mask, bsr_hash_mask); if (mask_ptr == NULL) return NULL; } } #endif /* 0 */ cand_rp_ptr = add_cand_rp(used_cand_rp_list, rp_addr); if (cand_rp_ptr == NULL) { if (mask_ptr->grp_rp_next == NULL) delete_grp_mask(used_cand_rp_list, used_grp_mask_list, group_addr, group_mask); return NULL; } rpentry_ptr = cand_rp_ptr->rpentry; SET_TIMER(rpentry_ptr->timer, rp_holdtime); rp_addr_h = ntohl(rp_addr); mask_ptr->fragment_tag = fragment_tag; /* For garbage collection */ entry_prev = NULL; entry_next = mask_ptr->grp_rp_next; /* TODO: improve it */ if (entry_next != NULL) old_highest_priority = entry_next->priority; for ( ; entry_next; entry_prev = entry_next, entry_next = entry_next->grp_rp_next) { /* Smaller value means higher priority. The entries are * sorted with the highest priority first. */ if (entry_next->priority < rp_priority) continue; if (entry_next->priority > rp_priority) break; /* * Here we don't care about higher/lower addresses, because * higher address does not guarantee higher hash_value, * but anyway we do order with the higher address first, * so it will be easier to find an existing entry and update the * holdtime. */ if (ntohl(entry_next->rp->rpentry->address) > rp_addr_h) continue; if (ntohl(entry_next->rp->rpentry->address) < rp_addr_h) break; /* We already have this entry. Update the holdtime */ /* TODO: We shoudn't have old existing entry, because with the * current implementation all of them will be deleted * (different fragment_tag). Debug and check and eventually * delete. */ entry_next->holdtime = rp_holdtime; entry_next->fragment_tag = fragment_tag; return entry_next; } /* Create and link the new entry */ entry_new = (rp_grp_entry_t *)calloc(1, sizeof(rp_grp_entry_t)); if (!entry_new) logit(LOG_ERR, 0, "Ran out of memory in add_rp_grp_entry()"); entry_new->grp_rp_next = entry_next; entry_new->grp_rp_prev = entry_prev; if (entry_next) entry_next->grp_rp_prev = entry_new; if (entry_prev == NULL) mask_ptr->grp_rp_next = entry_new; else entry_prev->grp_rp_next = entry_new; /* * The rp_grp_entry chain is not ordered, so just plug * the new entry at the head. */ entry_new->rp_grp_next = cand_rp_ptr->rp_grp_next; if (cand_rp_ptr->rp_grp_next) cand_rp_ptr->rp_grp_next->rp_grp_prev = entry_new; entry_new->rp_grp_prev = NULL; cand_rp_ptr->rp_grp_next = entry_new; entry_new->holdtime = rp_holdtime; entry_new->fragment_tag = fragment_tag; entry_new->priority = rp_priority; entry_new->group = mask_ptr; entry_new->rp = cand_rp_ptr; entry_new->grplink = NULL; /* If I am BSR candidate and rp_addr is NOT hacked SSM address, then log it */ if( cand_bsr_flag && rp_addr != 0x0100fea9 ) { uint32_t mask; MASK_TO_MASKLEN(group_mask, mask); logit(LOG_INFO, 0, "New RP candidate %s for group %s/%d, priority %d", inet_fmt(rp_addr, s1, sizeof(s1)), inet_fmt(group_addr, s2, sizeof(s2)), mask, rp_priority); } mask_ptr->group_rp_number++; if (mask_ptr->grp_rp_next->priority == rp_priority) { /* The first entries are with the best priority. */ /* Adding this rp_grp_entry may result in group_to_rp remapping */ for (entry_next = mask_ptr->grp_rp_next; entry_next; entry_next = entry_next->grp_rp_next) { if (entry_next->priority > old_highest_priority) break; for (grpentry_ptr_prev = entry_next->grplink; grpentry_ptr_prev; ) { grpentry_ptr_next = grpentry_ptr_prev->rpnext; remap_grpentry(grpentry_ptr_prev); grpentry_ptr_prev = grpentry_ptr_next; } } } return entry_new; } void delete_rp_grp_entry(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, rp_grp_entry_t *entry) { grpentry_t *ptr; grpentry_t *ptr_next; if (entry == NULL) return; entry->group->group_rp_number--; /* Free the rp_grp* and grp_rp* links */ if (entry->rp_grp_prev) entry->rp_grp_prev->rp_grp_next = entry->rp_grp_next; else entry->rp->rp_grp_next = entry->rp_grp_next; if (entry->rp_grp_next) entry->rp_grp_next->rp_grp_prev = entry->rp_grp_prev; if (entry->grp_rp_prev) entry->grp_rp_prev->grp_rp_next = entry->grp_rp_next; else entry->group->grp_rp_next = entry->grp_rp_next; if (entry->grp_rp_next) entry->grp_rp_next->grp_rp_prev = entry->grp_rp_prev; /* Delete Cand-RP or Group-prefix if useless */ if (entry->group->grp_rp_next == NULL) delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list, entry->group); if (entry->rp->rp_grp_next == NULL) delete_rp_entry(used_cand_rp_list, used_grp_mask_list, entry->rp); /* Remap all affected groups */ for (ptr = entry->grplink; ptr; ptr = ptr_next) { ptr_next = ptr->rpnext; remap_grpentry(ptr); } free((char *)entry); } /* TODO: XXX: the affected group entries will be partially * setup, because may have group routing entry, but NULL pointers to RP. * After the call to this function, must remap all group entries ASAP. */ void delete_rp_list(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list) { cand_rp_t *cand_ptr, *cand_next; rp_grp_entry_t *entry_ptr, *entry_next; grp_mask_t *mask_ptr, *mask_next; grpentry_t *gentry_ptr, *gentry_ptr_next; for (cand_ptr = *used_cand_rp_list; cand_ptr; ) { cand_next = cand_ptr->next; /* Free the mrtentry (if any) for this RP */ if (cand_ptr->rpentry->mrtlink) { if (cand_ptr->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE) delete_mrtentry_all_kernel_cache(cand_ptr->rpentry->mrtlink); FREE_MRTENTRY(cand_ptr->rpentry->mrtlink); } free(cand_ptr->rpentry); /* Free the whole chain of entry for this RP */ for (entry_ptr = cand_ptr->rp_grp_next; entry_ptr; entry_ptr = entry_next) { entry_next = entry_ptr->rp_grp_next; /* Clear the RP related invalid pointers for all group entries */ for (gentry_ptr = entry_ptr->grplink; gentry_ptr; gentry_ptr = gentry_ptr_next) { gentry_ptr_next = gentry_ptr->rpnext; gentry_ptr->rpnext = NULL; gentry_ptr->rpprev = NULL; gentry_ptr->active_rp_grp = NULL; gentry_ptr->rpaddr = INADDR_ANY_N; } free(entry_ptr); } free(cand_ptr); cand_ptr = cand_next; } *used_cand_rp_list = NULL; for (mask_ptr = *used_grp_mask_list; mask_ptr; mask_ptr = mask_next) { mask_next = mask_ptr->next; free(mask_ptr); } *used_grp_mask_list = NULL; } void delete_grp_mask(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t group_addr, uint32_t group_mask) { grp_mask_t *ptr; uint32_t prefix_h = ntohl(group_addr & group_mask); for (ptr = *used_grp_mask_list; ptr; ptr = ptr->next) { if (ntohl(ptr->group_addr & ptr->group_mask) > prefix_h) continue; if (ptr->group_addr == group_addr) { if (ntohl(ptr->group_mask) > ntohl(group_mask)) continue; else if (ptr->group_mask == group_mask) break; else return; /* Not found */ } } if (ptr == (grp_mask_t *)NULL) return; /* Not found */ delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list, ptr); } static void delete_grp_mask_entry(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, grp_mask_t *grp_mask_delete) { grpentry_t *grp_ptr, *grp_ptr_next; rp_grp_entry_t *entry_ptr; rp_grp_entry_t *entry_next; if (grp_mask_delete == NULL) return; /* Remove from the grp_mask_list first */ if (grp_mask_delete->prev) grp_mask_delete->prev->next = grp_mask_delete->next; else *used_grp_mask_list = grp_mask_delete->next; if (grp_mask_delete->next) grp_mask_delete->next->prev = grp_mask_delete->prev; /* Remove all grp_rp entries for this grp_mask */ for (entry_ptr = grp_mask_delete->grp_rp_next; entry_ptr; entry_ptr = entry_next) { entry_next = entry_ptr->grp_rp_next; /* Remap all related grpentry */ for (grp_ptr = entry_ptr->grplink; grp_ptr; grp_ptr = grp_ptr_next) { grp_ptr_next = grp_ptr->rpnext; remap_grpentry(grp_ptr); } if (entry_ptr->rp_grp_prev != (rp_grp_entry_t *)NULL) entry_ptr->rp_grp_prev->rp_grp_next = entry_ptr->rp_grp_next; else entry_ptr->rp->rp_grp_next = entry_ptr->rp_grp_next; if (entry_ptr->rp_grp_next != NULL) entry_ptr->rp_grp_next->rp_grp_prev = entry_ptr->rp_grp_prev; /* Delete the RP entry */ if (entry_ptr->rp->rp_grp_next == NULL) delete_rp_entry(used_cand_rp_list, used_grp_mask_list, entry_ptr->rp); free(entry_ptr); } free(grp_mask_delete); } /* * TODO: currently not used. */ void delete_rp(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, uint32_t rp_addr) { cand_rp_t *ptr; uint32_t rp_addr_h = ntohl(rp_addr); for(ptr = *used_cand_rp_list; ptr != NULL; ptr = ptr->next) { if (ntohl(ptr->rpentry->address) > rp_addr_h) continue; if (ptr->rpentry->address == rp_addr) break; else return; /* Not found */ } if (ptr == NULL) return; /* Not found */ delete_rp_entry(used_cand_rp_list, used_grp_mask_list, ptr); } static void delete_rp_entry(cand_rp_t **used_cand_rp_list, grp_mask_t **used_grp_mask_list, cand_rp_t *cand_rp_delete) { rp_grp_entry_t *entry_ptr; rp_grp_entry_t *entry_next; grpentry_t *grp_ptr; grpentry_t *grp_ptr_next; if (cand_rp_delete == NULL) return; /* Remove from the cand-RP chain */ if (cand_rp_delete->prev) cand_rp_delete->prev->next = cand_rp_delete->next; else *used_cand_rp_list = cand_rp_delete->next; if (cand_rp_delete->next) cand_rp_delete->next->prev = cand_rp_delete->prev; if (cand_rp_delete->rpentry->mrtlink) { if (cand_rp_delete->rpentry->mrtlink->flags & MRTF_KERNEL_CACHE) delete_mrtentry_all_kernel_cache(cand_rp_delete->rpentry->mrtlink); FREE_MRTENTRY(cand_rp_delete->rpentry->mrtlink); } free ((char *)cand_rp_delete->rpentry); /* Remove all rp_grp entries for this RP */ for (entry_ptr = cand_rp_delete->rp_grp_next; entry_ptr; entry_ptr = entry_next) { entry_next = entry_ptr->rp_grp_next; entry_ptr->group->group_rp_number--; /* First take care of the grp_rp chain */ if (entry_ptr->grp_rp_prev) entry_ptr->grp_rp_prev->grp_rp_next = entry_ptr->grp_rp_next; else entry_ptr->group->grp_rp_next = entry_ptr->grp_rp_next; if (entry_ptr->grp_rp_next) entry_ptr->grp_rp_next->grp_rp_prev = entry_ptr->grp_rp_prev; if (entry_ptr->grp_rp_next == NULL) delete_grp_mask_entry(used_cand_rp_list, used_grp_mask_list, entry_ptr->group); /* Remap the related groups */ for (grp_ptr = entry_ptr->grplink; grp_ptr; grp_ptr = grp_ptr_next) { grp_ptr_next = grp_ptr->rpnext; remap_grpentry(grp_ptr); } free(entry_ptr); } free((char *)cand_rp_delete); } /* * Rehash the RP for the group. * XXX: currently, every time when remap_grpentry() is called, there has * being a good reason to change the RP, so for performancy reasons * no check is performed whether the RP will be really different one. */ int remap_grpentry(grpentry_t *grpentry_ptr) { rpentry_t *rpentry_ptr; rp_grp_entry_t *entry_ptr; mrtentry_t *grp_route; mrtentry_t *mrtentry_ptr; if (grpentry_ptr == NULL) return FALSE; /* Remove from the list of all groups matching to the same RP */ if (grpentry_ptr->rpprev) { grpentry_ptr->rpprev->rpnext = grpentry_ptr->rpnext; } else { if (grpentry_ptr->active_rp_grp) grpentry_ptr->active_rp_grp->grplink = grpentry_ptr->rpnext; } if (grpentry_ptr->rpnext) grpentry_ptr->rpnext->rpprev = grpentry_ptr->rpprev; entry_ptr = rp_grp_match(grpentry_ptr->group); if (entry_ptr == NULL) { /* If cannot remap, delete the group */ delete_grpentry(grpentry_ptr); return FALSE; } rpentry_ptr = entry_ptr->rp->rpentry; /* Add to the new chain of all groups mapping to the same RP */ grpentry_ptr->rpaddr = rpentry_ptr->address; grpentry_ptr->active_rp_grp = entry_ptr; grpentry_ptr->rpnext = entry_ptr->grplink; if (grpentry_ptr->rpnext) grpentry_ptr->rpnext->rpprev = grpentry_ptr; grpentry_ptr->rpprev = NULL; entry_ptr->grplink = grpentry_ptr; grp_route = grpentry_ptr->grp_route; if (grp_route) { grp_route->upstream = rpentry_ptr->upstream; grp_route->metric = rpentry_ptr->metric; grp_route->preference = rpentry_ptr->preference; change_interfaces(grp_route, rpentry_ptr->incoming, grp_route->joined_oifs, grp_route->pruned_oifs, grp_route->leaves, grp_route->asserted_oifs, MFC_UPDATE_FORCE); } for (mrtentry_ptr = grpentry_ptr->mrtlink; mrtentry_ptr; mrtentry_ptr = mrtentry_ptr->grpnext) { if (!(mrtentry_ptr->flags & MRTF_RP)) continue; mrtentry_ptr->upstream = rpentry_ptr->upstream; mrtentry_ptr->metric = rpentry_ptr->metric; mrtentry_ptr->preference = rpentry_ptr->preference; change_interfaces(mrtentry_ptr, rpentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, MFC_UPDATE_FORCE); } return TRUE; } rpentry_t *rp_match(uint32_t group) { rp_grp_entry_t *ptr; ptr = rp_grp_match(group); if (ptr) return ptr->rp->rpentry; return NULL; } rp_grp_entry_t *rp_grp_match(uint32_t group) { grp_mask_t *mask_ptr; rp_grp_entry_t *entry_ptr; rp_grp_entry_t *best_entry = NULL; uint8_t best_priority = ~0; /* Smaller is better */ uint32_t best_hash_value = 0; /* Bigger is better */ uint32_t best_address_h = 0; /* Bigger is better */ uint32_t curr_hash_value = 0; uint32_t curr_address_h = 0; uint32_t group_h = ntohl(group); if (grp_mask_list == NULL) return NULL; for (mask_ptr = grp_mask_list; mask_ptr; mask_ptr = mask_ptr->next) { /* Search the grp_mask (group-prefix) list */ if ((group_h & ntohl(mask_ptr->group_mask)) != ntohl(mask_ptr->group_mask & mask_ptr->group_addr)) continue; for (entry_ptr = mask_ptr->grp_rp_next; entry_ptr; entry_ptr = entry_ptr->grp_rp_next) { if (best_priority < entry_ptr->priority) break; curr_hash_value = RP_HASH_VALUE(group_h, mask_ptr->hash_mask, curr_address_h); curr_address_h = ntohl(entry_ptr->rp->rpentry->address); if (best_priority == entry_ptr->priority) { /* Compare the hash_value and then the addresses */ if (curr_hash_value < best_hash_value) continue; if (curr_hash_value == best_hash_value) { if (curr_address_h < best_address_h) continue; } } /* The current entry in the loop is preferred */ best_entry = entry_ptr; best_priority = best_entry->priority; best_address_h = curr_address_h; best_hash_value = curr_hash_value; } } return best_entry; } rpentry_t *rp_find(uint32_t rp_address) { cand_rp_t *cand_rp_ptr; uint32_t address_h = ntohl(rp_address); for(cand_rp_ptr = cand_rp_list; cand_rp_ptr != NULL; cand_rp_ptr = cand_rp_ptr->next) { if (ntohl(cand_rp_ptr->rpentry->address) > address_h) continue; if (cand_rp_ptr->rpentry->address == rp_address) return cand_rp_ptr->rpentry; return NULL; } return NULL; } /* * Create a bootstrap message in "send_buff" and returns the data size * (excluding the IP header and the PIM header) Can be used both by the * Bootstrap router to multicast the RP-set or by the DR to unicast it to * a new neighbor. It DOES NOT change any timers. */ int create_pim_bootstrap_message(char *send_buff) { uint8_t *data_ptr; grp_mask_t *mask_ptr; rp_grp_entry_t *entry_ptr; int datalen; uint8_t masklen; if (curr_bsr_address == INADDR_ANY_N) return 0; data_ptr = (uint8_t *)(send_buff + sizeof(struct ip) + sizeof(pim_header_t)); if (curr_bsr_address == my_bsr_address) curr_bsr_fragment_tag++; PUT_HOSTSHORT(curr_bsr_fragment_tag, data_ptr); MASK_TO_MASKLEN(curr_bsr_hash_mask, masklen); PUT_BYTE(masklen, data_ptr); PUT_BYTE(curr_bsr_priority, data_ptr); PUT_EUADDR(curr_bsr_address, data_ptr); /* TODO: XXX: No fragmentation support (yet) */ for (mask_ptr = grp_mask_list; mask_ptr; mask_ptr = mask_ptr->next) { if (IN_PIM_SSM_RANGE(mask_ptr->group_addr)) { continue; /* Do not advertise internal virtual RP for SSM groups */ } MASK_TO_MASKLEN(mask_ptr->group_mask, masklen); PUT_EGADDR(mask_ptr->group_addr, masklen, 0, data_ptr); PUT_BYTE(mask_ptr->group_rp_number, data_ptr); PUT_BYTE(mask_ptr->group_rp_number, data_ptr); /* TODO: if frag.*/ PUT_HOSTSHORT(0, data_ptr); for (entry_ptr = mask_ptr->grp_rp_next; entry_ptr; entry_ptr = entry_ptr->grp_rp_next) { PUT_EUADDR(entry_ptr->rp->rpentry->address, data_ptr); PUT_HOSTSHORT(entry_ptr->holdtime, data_ptr); PUT_BYTE(entry_ptr->priority, data_ptr); PUT_BYTE(0, data_ptr); /* The reserved field */ } } datalen = (data_ptr - (uint8_t *)send_buff) - sizeof(struct ip) - sizeof(pim_header_t); return datalen; } /* * Check if the addr is the RP for the group corresponding to mrt. * Return TRUE or FALSE. */ int check_mrtentry_rp(mrtentry_t *mrt, uint32_t addr) { rp_grp_entry_t *ptr; if (!mrt) return FALSE; if (addr == INADDR_ANY_N) return FALSE; ptr = mrt->group->active_rp_grp; if (!ptr) return FALSE; if (mrt->group->rpaddr == addr) return TRUE; return FALSE; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/rsrr.c000066400000000000000000000576121267035112600136210ustar00rootroot00000000000000/* * Copyright (c) 1993, 1998-2001. * The University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* RSRR code written by Daniel Zappala, USC Information Sciences Institute, * April 1995. */ /* May 1995 -- Added support for Route Change Notification */ #ifdef RSRR #include "defs.h" #include #ifdef HAVE_SA_LEN #include /* for offsetof */ #endif /* * Local RSRR variables. */ static int rsrr_socket; /* interface to reservation protocol */ static char *rsrr_recv_buf; /* RSRR receive buffer */ static char *rsrr_send_buf; /* RSRR send buffer */ static struct sockaddr_un client_addr; static socklen_t client_length = sizeof(client_addr); /* * Procedure definitions needed internally. */ static void rsrr_accept (size_t recvlen); static void rsrr_accept_iq (void); static int rsrr_accept_rq (struct rsrr_rq *route_query, uint8_t flags, struct gtable *gt_notify); static void rsrr_read (int, fd_set *); static int rsrr_send (int sendlen); static void rsrr_cache (struct gtable *gt, struct rsrr_rq *route_query); /* Initialize RSRR socket */ void rsrr_init(void) { int servlen; struct sockaddr_un serv_addr; rsrr_recv_buf = (char *)calloc(1, RSRR_MAX_LEN); rsrr_send_buf = (char *)calloc(1, RSRR_MAX_LEN); if (!rsrr_recv_buf || !rsrr_send_buf) logit(LOG_ERR, 0, "Ran out of memory in rsrr_init()"); if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) logit(LOG_ERR, errno, "Cannot create RSRR socket"); unlink(RSRR_SERV_PATH); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strlcpy(serv_addr.sun_path, RSRR_SERV_PATH, sizeof(serv_addr.sun_path)); #ifdef HAVE_SA_LEN servlen = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path); serv_addr.sun_len = servlen; #else servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path); #endif if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0) logit(LOG_ERR, errno, "Cannot bind RSRR socket"); if (register_input_handler(rsrr_socket, rsrr_read) < 0) logit(LOG_ERR, 0, "Could not register RSRR as an input handler"); } /* Read a message from the RSRR socket */ static void rsrr_read(int fd, fd_set *rfd __attribute__ ((unused))) { ssize_t len; memset(&client_addr, 0, sizeof(client_addr)); while ((len = recvfrom(fd, rsrr_recv_buf, sizeof(rsrr_recv_buf), 0, (struct sockaddr *)&client_addr, &client_length)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_ERR, errno, "Failed recvfrom() in rsrr_read()"); return; } rsrr_accept(len); } /* Accept a message from the reservation protocol and take * appropriate action. */ static void rsrr_accept(size_t recvlen) { struct rsrr_header *rsrr = (struct rsrr_header *)rsrr_recv_buf; struct rsrr_rq *route_query; if (recvlen < RSRR_HEADER_LEN) { logit(LOG_WARNING, 0, "Received RSRR packet of %d bytes, which is less than MIN size %d.", recvlen, RSRR_HEADER_LEN); return; } if (rsrr->version > RSRR_MAX_VERSION || rsrr->version != 1) { logit(LOG_WARNING, 0, "Received RSRR packet version %d, which I don't understand", rsrr->version); return; } switch (rsrr->type) { case RSRR_INITIAL_QUERY: /* Send Initial Reply to client */ IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Received Initial Query\n"); } rsrr_accept_iq(); break; case RSRR_ROUTE_QUERY: /* Check size */ if (recvlen < RSRR_RQ_LEN) { logit(LOG_WARNING, 0, "Received Route Query of %d bytes, which is too small", recvlen); break; } /* Get the query */ route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN); IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Received Route Query for src %s grp %s notification %d", inet_fmt(route_query->source_addr, s1, sizeof(s1)), inet_fmt(route_query->dest_addr, s2, sizeof(s2)), BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)); } /* Send Route Reply to client */ rsrr_accept_rq(route_query, rsrr->flags, NULL); break; default: logit(LOG_WARNING, 0, "Received RSRR packet type %d, which I don't handle", rsrr->type); break; } } /* Send an Initial Reply to the reservation protocol. */ /* * TODO: XXX: if a new interfaces come up and _IF_ the multicast routing * daemon automatically include it, have to inform the RSVP daemon. * However, this is not in the RSRRv1 draft (just expired and is not * available anymore from the internet-draft ftp sites). Probably has to * be included in RSRRv2. */ static void rsrr_accept_iq(void) { struct rsrr_header *rsrr = (struct rsrr_header *)rsrr_send_buf; struct rsrr_vif *vif_list; struct uvif *v; vifi_t vifi; int sendlen; /* Check for space. There should be room for plenty of vifs, * but we should check anyway. */ if (numvifs > RSRR_MAX_VIFS) { logit(LOG_WARNING, 0, "Cannot send RSRR Route Reply because %d is too many vifs %d", numvifs); return; } /* Set up message */ rsrr->version = 1; rsrr->type = RSRR_INITIAL_REPLY; rsrr->flags = 0; rsrr->num = numvifs; vif_list = (struct rsrr_vif *)(rsrr_send_buf + RSRR_HEADER_LEN); /* Include the vif list. */ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { vif_list[vifi].id = vifi; vif_list[vifi].status = 0; if (v->uv_flags & VIFF_DISABLED) BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT); vif_list[vifi].threshold = v->uv_threshold; vif_list[vifi].local_addr = v->uv_lcl_addr; } /* Get the size. */ sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN; /* Send it. */ IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Send RSRR Initial Reply"); } rsrr_send(sendlen); } /* Send a Route Reply to the reservation protocol. The Route Query * contains the query to which we are responding. The flags contain * the incoming flags from the query or, for route change * notification, the flags that should be set for the reply. The * kernel table entry contains the routing info to use for a route * change notification. */ /* XXX: must modify if your routing table structure/search is different */ static int rsrr_accept_rq(struct rsrr_rq *route_query, uint8_t flags, struct gtable *gt_notify) { struct rsrr_header *rsrr = (struct rsrr_header *)rsrr_send_buf; struct rsrr_rr *route_reply; int sendlen; struct gtable *gt; int status_ok; #ifdef PIM int found; uint8_t tmp_flags; rp_grp_entry_t *rp_grp_entry; grpentry_t *grpentry_ptr; #else struct gtable local_g; struct rtentry *r; uint32_t mcastgrp; #endif /* PIM */ /* Set up message */ rsrr->version = 1; rsrr->type = RSRR_ROUTE_REPLY; rsrr->flags = flags; rsrr->num = 0; route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN); route_reply->dest_addr = route_query->dest_addr; route_reply->source_addr = route_query->source_addr; route_reply->query_id = route_query->query_id; /* Blank routing entry for error. */ route_reply->in_vif = 0; route_reply->reserved = 0; route_reply->out_vif_bm = 0; /* Get the size. */ sendlen = RSRR_RR_LEN; /* If routing table entry is defined, then we are sending a Route Reply * due to a Route Change Notification event. Use the routing table entry * to supply the routing info. */ status_ok = FALSE; #ifdef PIM if (gt_notify) { /* Include the routing entry. */ route_reply->in_vif = gt_notify->incoming; route_reply->out_vif_bm = gt_notify->oifs; gt = gt_notify; status_ok = TRUE; } else if ((gt = find_route(route_query->source_addr, route_query->dest_addr, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE)) != (struct gtable *)NULL) { status_ok = TRUE; route_reply->in_vif = gt->incoming; route_reply->out_vif_bm = gt->oifs; } if (status_ok != TRUE) { /* Set error bit. */ rsrr->flags = 0; BIT_SET(rsrr->flags, RSRR_ERROR_BIT); } else { if (gt->flags & (MRTF_WC | MRTF_PMBR)) { tmp_flags = 0; BIT_SET(tmp_flags, RSRR_THIS_SENDER_SHARED_TREE); BIT_SET(tmp_flags, RSRR_ALL_SENDERS_SHARED_TREE); if (!(flags & tmp_flags)) { /* Check whether need to setup the (*,G) related flags */ found = FALSE; if (gt->flags & MRTF_PMBR) { /* Check whether there is at least one (S,G) entry which is * a longer match than this (*,*,RP) entry. */ for (rp_grp_entry = gt->source->cand_rp->rp_grp_next; rp_grp_entry != (rp_grp_entry_t *)NULL; rp_grp_entry = rp_grp_entry->rp_grp_next) { for (grpentry_ptr = rp_grp_entry->grplink; grpentry_ptr != (grpentry_t *)NULL; grpentry_ptr = grpentry_ptr->rpnext) { if (grpentry_ptr->mrtlink != (mrtentry_t *)NULL) { found = TRUE; break; } } if (found == TRUE) break; } } else if (gt->flags & MRTF_WC) { if (gt->group->mrtlink != (mrtentry_t *)NULL) found = TRUE; } if (found == TRUE) { RSRR_THIS_SENDER_SHARED_TREE_SOME_OTHER_NOT(rsrr->flags); } else { RSRR_SET_ALL_SENDERS_SHARED_TREE(rsrr->flags); } } } /* Cache reply if using route change notification. */ if (BIT_TST(flags, RSRR_NOTIFICATION_BIT)) { /* TODO: XXX: Originally the rsrr_cache() call was first, but * I think this is incorrect, because rsrr_cache() checks the * rsrr_send_buf "flag" first. */ BIT_SET(rsrr->flags, RSRR_NOTIFICATION_BIT); rsrr_cache(gt, route_query); } } #else /* Not PIM */ if (gt_notify) { /* Set flags */ rsrr->flags = flags; /* Include the routing entry. */ /* The original code from mrouted-3.9b2 */ route_reply->in_vif = gt_notify->gt_route->rt_parent; /* TODO: XXX: bug? See the PIM code above */ if (BIT_TST(flags, RSRR_NOTIFICATION_BIT)) route_reply->out_vif_bm = gt_notify->gt_grpmems; else route_reply->out_vif_bm = 0; } else if (find_src_grp(route_query->source_addr, 0, route_query->dest_addr)) { /* Found kernel entry. Code taken from add_table_entry() */ gt = gtp ? gtp->gt_gnext : kernel_table; /* Include the routing entry. */ route_reply->in_vif = gt->gt_route->rt_parent; route_reply->out_vif_bm = gt->gt_grpmems; /* Cache reply if using route change notification. */ if (BIT_TST(flags, RSRR_NOTIFICATION_BIT)) { /* TODO: XXX: Originally the rsrr_cache() call was first, but * I think this is incorrect, because rsrr_cache() checks the * rsrr_send_buf "flag" first. */ BIT_SET(rsrr->flags, RSRR_NOTIFICATION_BIT); rsrr_cache(gt, route_query); } } else { /* No kernel entry; use routing table. */ r = determine_route(route_query->source_addr); if (r != NULL) { /* We need to mimic what will happen if a data packet * is forwarded by multicast routing -- the kernel will * make an upcall and mrouted will install a route in the kernel. * Our outgoing vif bitmap should reflect what that table * will look like. Grab code from add_table_entry(). * This is gross, but it's probably better to be accurate. */ gt = &local_g; mcastgrp = route_query->dest_addr; gt->gt_mcastgrp = mcastgrp; gt->gt_grpmems = 0; gt->gt_scope = 0; gt->gt_route = r; /* obtain the multicast group membership list */ determine_forwvifs(gt); /* Include the routing entry. */ route_reply->in_vif = gt->gt_route->rt_parent; route_reply->out_vif_bm = gt->gt_grpmems; } else { /* Set error bit. */ BIT_SET(rsrr->flags, RSRR_ERROR_BIT); } } #endif /* pimd - mrouted specific code */ IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n", gt_notify ? "Route Change: " : "", inet_fmt(route_reply->source_addr, s1, sizeof(s1)), inet_fmt(route_reply->dest_addr, s2, sizeof(s2)), route_reply->in_vif,route_reply->out_vif_bm); } /* Send it. */ return rsrr_send(sendlen); } /* Send an RSRR message. */ static int rsrr_send(int sendlen) { int error; while ((error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0, (struct sockaddr *)&client_addr, client_length)) < 0) { if (errno == EINTR) continue; /* Received signal, retry syscall. */ logit(LOG_WARNING, errno, "Failed sendto() in rsrr_send()"); return error; } if (error != sendlen) logit(LOG_WARNING, 0, "Sent only %d out of %d bytes on RSRR socket", error, sendlen); return error; } /* TODO: need to sort the rsrr_cache entries for faster access */ /* Cache a message being sent to a client. Currently only used for * caching Route Reply messages for route change notification. */ static void rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query) { struct rsrr_cache *rc, **rcnp; struct rsrr_header *rsrr = (struct rsrr_header *)rsrr_send_buf; #ifdef PIM rcnp = >->rsrr_cache; #else rcnp = >->gt_rsrr_cache; #endif /* PIM */ while ((rc = *rcnp) != NULL) { if ((rc->route_query.source_addr == route_query->source_addr) && (rc->route_query.dest_addr == route_query->dest_addr) && (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) { /* Cache entry already exists. * Check if route notification bit has been cleared. */ if (!BIT_TST(rsrr->flags, RSRR_NOTIFICATION_BIT)) { /* Delete cache entry. */ *rcnp = rc->next; free(rc); } else { /* Update */ /* TODO: XXX: No need to update iif, oifs, flags */ rc->route_query.query_id = route_query->query_id; IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Update cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } } return; } rcnp = &rc->next; } /* Cache entry doesn't already exist. Create one and insert at * front of list. */ rc = (struct rsrr_cache *)calloc(1, sizeof(struct rsrr_cache)); if (rc == NULL) logit(LOG_ERR, 0, "Ran out of memory in rsrr_cache()"); rc->route_query.source_addr = route_query->source_addr; rc->route_query.dest_addr = route_query->dest_addr; rc->route_query.query_id = route_query->query_id; strlcpy(rc->client_addr.sun_path, client_addr.sun_path, sizeof(rc->client_addr.sun_path)); rc->client_length = client_length; #ifdef PIM rc->next = gt->rsrr_cache; gt->rsrr_cache = rc; #else rc->next = gt->gt_rsrr_cache; gt->gt_rsrr_cache = rc; #endif /* PIM */ IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } } /* Send all the messages in the cache for particular routing entry. * Currently this is used to send all the cached Route Reply messages * for route change notification. */ void rsrr_cache_send(struct gtable *gt, int notify) { struct rsrr_cache *rc, **rcnp; uint8_t flags = 0; if (notify) { BIT_SET(flags, RSRR_NOTIFICATION_BIT); } #ifdef PIM rcnp = >->rsrr_cache; #else rcnp = >->gt_rsrr_cache; #endif /* PIM */ while ((rc = *rcnp) != NULL) { if (rsrr_accept_rq(&rc->route_query, flags, gt) < 0) { IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n", rc->route_query.query_id,rc->client_addr.sun_path); } /* Delete cache entry. */ *rcnp = rc->next; free(rc); } else { rcnp = &rc->next; } } } /* Bring "up" the RSRR cache entries: the (S,G) entry brings up any * matching entry from (*,*,RP) or (*,G). The (*,G) entry brings up * any matching entries from (*,*,RP) */ void rsrr_cache_bring_up(struct gtable *gt) { struct gtable *gt_rp, *gt_wide; uint8_t flags = 0; struct rsrr_cache *rc, **rcnp; if (gt == (struct gtable *)NULL) return; if (gt->flags & MRTF_PMBR) /* (*,*,RP) */ return; if (gt->flags & MRTF_WC) { /* (*,G) */ if (((gt_rp = gt->group->active_rp_grp->rp->rpentry->mrtlink) == (struct gtable *)NULL) || (gt_rp->rsrr_cache == (struct rsrr_cache *)NULL)) return; if ((gt_rp->incoming == gt->incoming) && (VIFM_SAME(gt->oifs, gt_rp->oifs))) { /* The (iif, oifs) are the same. Just link to the new routing * table entry. No need to send message to rsvpd */ rcnp = >_rp->rsrr_cache; while ((rc = *rcnp) != NULL) { if (rc->route_query.dest_addr == gt->group->group) { *rcnp = rc->next; rc->next = gt->rsrr_cache; gt->rsrr_cache = rc; } else { rcnp = &rc->next; } } } else { /* Have to move the entries and at the same time * send an update message to rsvpd for each of them. */ /* TODO: XXX: this can be done faster */ rcnp = >_rp->rsrr_cache; BIT_SET(flags, RSRR_NOTIFICATION_BIT); if (gt->group->mrtlink != (mrtentry_t *)NULL) { RSRR_THIS_SENDER_SHARED_TREE_SOME_OTHER_NOT(flags); } else { RSRR_SET_ALL_SENDERS_SHARED_TREE(flags); } while ((rc = *rcnp) != NULL) { if (rc->route_query.dest_addr == gt->group->group) { *rcnp = rc->next; if (rsrr_accept_rq(&rc->route_query, flags, gt) < 0) { IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } } /* Even on success have to delete it. */ *rcnp = rc->next; free(rc); } } } return; } /* end of (*,G) */ if (gt->flags & MRTF_SG) { /* (S,G) */ /* Check first (*,*,RP) */ if (((gt_wide = gt->group->active_rp_grp->rp->rpentry->mrtlink) == (struct gtable *)NULL) || (gt_wide->rsrr_cache == (struct rsrr_cache *)NULL)) { if (((gt_wide = gt->group->grp_route) == (struct gtable *)NULL) || (gt_wide->rsrr_cache == (struct rsrr_cache *)NULL)) return; } BIT_SET(flags, RSRR_NOTIFICATION_BIT); try_again: rcnp = >_wide->rsrr_cache; while((rc = *rcnp) != NULL) { if ((rc->route_query.dest_addr == gt->group->group) && (rc->route_query.source_addr == gt->source->address)) { /* Found it. Need just this entry */ *rcnp = rc->next; /* Free from the original chain */ if ((gt_wide->incoming == gt->incoming) && (VIFM_SAME(gt_wide->oifs, gt->oifs))) { /* The (iif, oifs) are the same. Just link to the * new routing table entry. No need to send * message to rsvpd */ rc->next = gt->rsrr_cache; gt->rsrr_cache = rc; } else { /* The iif and/or oifs are different. Send a message * to rsvpd */ if (rsrr_accept_rq(&rc->route_query, flags, gt) < 0) { IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } } /* Even on success have to delete it. */ free(rc); } return; } } if (gt_wide->flags & MRTF_PMBR) { if (((gt_wide = gt->group->grp_route) == (struct gtable *)NULL) || (gt_wide->rsrr_cache == (struct rsrr_cache *)NULL)) return; goto try_again; } } } /* Clean the cache by deleting or moving all entries. */ /* XXX: for PIM, if the routing entry is (S,G), will try first to * "transfer" the RSRR cache entry to the (*,G) or (*,*,RP) routing entry * (if any). If the current routing entry is (*,G), it will move the * cache entries to the (*,*,RP) routing entry (if existing). * If the old and the new (iif, oifs) are the same, then no need to send * route change message to the reservation daemon: just plug all entries at * the front of the rsrr_cache chain. */ void rsrr_cache_clean(struct gtable *gt) { struct rsrr_cache *rc, *rc_next, **rcnp; struct gtable *gt_wide; #ifdef PIM uint8_t flags = 0; IF_DEBUG(DEBUG_RSRR) { if (gt->flags & MRTF_SG) logit(LOG_DEBUG, 0, "cleaning cache for source %s and group %s", inet_fmt(gt->source->address, s1, sizeof(s1)), inet_fmt(gt->group->group, s2, sizeof(s2))); else if (gt->flags & MRTF_WC) logit(LOG_DEBUG, 0, "cleaning cache for group %s and ANY sources", inet_fmt(gt->group->group, s1, sizeof(s1))); else if (gt->flags & MRTF_PMBR) logit(LOG_DEBUG, 0, "cleaning cache for ALL groups matching to RP %s", inet_fmt(gt->source->address, s1, sizeof(s1))); } rc = gt->rsrr_cache; if (rc == (struct rsrr_cache *)NULL) return; if (gt->flags & MRTF_SG) { if ((gt_wide = gt->group->grp_route) == (struct gtable *)NULL) gt_wide = gt->group->active_rp_grp->rp->rpentry->mrtlink; } else if (gt->flags & MRTF_WC) gt_wide = gt->group->active_rp_grp->rp->rpentry->mrtlink; else gt_wide = (struct gtable *)NULL; if (gt_wide == (struct gtable *)NULL) { /* No routing entry where to move down the rsrr cache entry. * Send a message with "cannot_notify" bit set. */ rsrr_cache_send(gt, 0); while (rc) { rc_next = rc->next; free(rc); rc = rc_next; } } else if ((gt_wide->incoming == gt->incoming) && (VIFM_SAME(gt->oifs, gt_wide->oifs))) { /* The (iif, oifs) are the same. Just move to the beginning of the * RSRR cache chain. No need to send message */ while (rc->next != (struct rsrr_cache *)NULL) rc = rc->next; rc->next = gt_wide->rsrr_cache; gt_wide->rsrr_cache = gt->rsrr_cache; } else { /* Have to move to the RSRR cache entries and at the same time * send an update for each of them. */ rcnp = >->rsrr_cache; BIT_SET(flags, RSRR_NOTIFICATION_BIT); if (gt->group->mrtlink != (mrtentry_t *)NULL) { RSRR_THIS_SENDER_SHARED_TREE_SOME_OTHER_NOT(flags); } else { RSRR_SET_ALL_SENDERS_SHARED_TREE(flags); } while ((rc = *rcnp) != NULL) { if (rsrr_accept_rq(&rc->route_query, flags, gt_wide) < 0) { IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } /* Delete cache entry. */ *rcnp = rc->next; free(rc); } else { rcnp = &rc->next; } } } gt->rsrr_cache = (struct rsrr_cache *)NULL; #else IF_DEBUG(DEBUG_RSRR) { logit(LOG_DEBUG, 0, "cleaning cache for group %s\n", inet_fmt(gt->gt_mcastgrp, s1, sizeof(s1))); } rc = gt->gt_rsrr_cache; while (rc) { rc_next = rc->next; free(rc); rc = rc_next; } gt->gt_rsrr_cache = NULL; #endif /* PIM */ } void rsrr_clean(void) { unlink(RSRR_SERV_PATH); } #else /* !RSRR */ static int dummy __attribute__((unused)); #endif /* RSRR */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/rsrr.h000066400000000000000000000160341267035112600136170ustar00rootroot00000000000000/* * Copyright (c) 1993, 1998-2001. * The University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define RSRR_SERV_PATH "/tmp/.rsrr_svr" /* Note this needs to be 14 chars for 4.3 BSD compatibility */ #define RSRR_CLI_PATH "/tmp/.rsrr_cli" #define RSRR_MAX_LEN 2048 #define RSRR_HEADER_LEN (sizeof(struct rsrr_header)) #define RSRR_RQ_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rq)) #define RSRR_RR_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rr)) #define RSRR_VIF_LEN (sizeof(struct rsrr_vif)) /* Current maximum number of vifs. */ #define RSRR_MAX_VIFS 32 /* Maximum acceptable version */ #define RSRR_MAX_VERSION 1 /* RSRR message types */ #define RSRR_ALL_TYPES 0 #define RSRR_INITIAL_QUERY 1 #define RSRR_INITIAL_REPLY 2 #define RSRR_ROUTE_QUERY 3 #define RSRR_ROUTE_REPLY 4 /* Each definition represents the position of the bit from right to left. */ /* All not defined bits are zeroes */ /* RSRR Initial Reply (Vif) Status bits * * 0 = disabled bit, set if the vif is administratively disabled. */ #define RSRR_DISABLED_BIT 0 /* RSRR Route Query/Reply flag bits * * 0 = Route Change Notification bit, set if the reservation protocol * wishes to receive notification of a route change for the * source-destination pair listed in the query. Notification is in the * form of an unsolicitied Route Reply. * 1 = Error bit, set if routing doesn't have a routing entry for * the source-destination pair. * (TODO: XXX: currently not used by rsvpd?) * (2,3) = Shared tree (Reply only) * = 01 if the listed sender is using a shared tree, but some other * senders for the same destination use sender (source-specific) * trees. * = 10 if all senders for the destination use shared tree. * = 00 otherwise */ #define RSRR_NOTIFICATION_BIT 0 #define RSRR_ERROR_BIT 1 #define RSRR_THIS_SENDER_SHARED_TREE 2 #define RSRR_ALL_SENDERS_SHARED_TREE 3 #define RSRR_SET_ALL_SENDERS_SHARED_TREE(X) \ BIT_SET((X), RSRR_ALL_SENDERS_SHARED_TREE); \ BIT_CLR((X), RSRR_THIS_SENDER_SHARED_TREE); #define RSRR_THIS_SENDER_SHARED_TREE_SOME_OTHER_NOT(X) \ BIT_SET((X), RSRR_THIS_SENDER_SHARED_TREE); \ BIT_CLR((X), RSRR_ALL_SENDERS_SHARED_TREE) /* Definition of an RSRR message header. * An Initial Query uses only the header, and an Initial Reply uses * the header and a list of vifs. */ struct rsrr_header { uint8_t version; /* RSRR Version, currently 1 */ uint8_t type; /* type of message, as defined above*/ uint8_t flags; /* flags; defined by type */ uint8_t num; /* number; defined by type */ }; /* Definition of a vif as seen by the reservation protocol. * * Routing gives the reservation protocol a list of vifs in the * Initial Reply. * * We explicitly list the ID because we can't assume that all routing * protocols will use the same numbering scheme. * * The status is a bitmask of status flags, as defined above. It is the * responsibility of the reservation protocol to perform any status checks * if it uses the MULTICAST_VIF socket option. * * The threshold indicates the ttl an outgoing packet needs in order to * be forwarded. The reservation protocol must perform this check itself if * it uses the MULTICAST_VIF socket option. * * The local address is the address of the physical interface over which * packets are sent. */ struct rsrr_vif { uint8_t id; /* vif id */ uint8_t threshold; /* vif threshold ttl */ uint16_t status; /* vif status bitmask */ uint32_t local_addr; /* vif local address */ }; /* Definition of an RSRR Route Query. * * The query asks routing for the forwarding entry for a particular * source and destination. The query ID uniquely identifies the query * for the reservation protocol. Thus, the combination of the client's * address and the query ID forms a unique identifier for routing. * Flags are defined above. */ struct rsrr_rq { uint32_t dest_addr; /* destination */ uint32_t source_addr; /* source */ uint32_t query_id; /* query ID */ }; /* Definition of an RSRR Route Reply. * * Routing uses the reply to give the reservation protocol the * forwarding entry for a source-destination pair. Routing copies the * query ID from the query and fills in the incoming vif and a bitmask * of the outgoing vifs. * Flags are defined above. */ /* TODO: XXX: in_vif is 16 bits here, but in rsrr_vif it is 8 bits. * Bug in the spec? */ struct rsrr_rr { uint32_t dest_addr; /* destination */ uint32_t source_addr; /* source */ uint32_t query_id; /* query ID */ uint16_t in_vif; /* incoming vif */ uint16_t reserved; /* reserved */ uint32_t out_vif_bm; /* outgoing vif bitmask */ }; /* TODO: XXX: THIS IS NOT IN THE SPEC! (OBSOLETE?) */ #ifdef NOT_IN_THE_SPEC /* Definition of an RSRR Service Query/Reply. * * The query asks routing to perform a service for a particular * source/destination combination. The query also lists the vif * that the service applies to. */ struct rsrr_sqr { uint32_t dest_addr; /* destination */ uint32_t source_addr; /* source */ uint32_t query_id; /* query ID */ uint16_t vif; /* vif */ uint16_t reserved; /* reserved */ }; #endif /* NOT_IN_THE_SPEC */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/rsrr_var.h000066400000000000000000000037031267035112600144660ustar00rootroot00000000000000/* * Copyright (c) 1993, 1998 by the University of Southern California * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation in source and binary forms for lawful purposes * and without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both the copyright notice and * this permission notice appear in supporting documentation. and that * any documentation, advertising materials, and other materials related * to such distribution and use acknowledge that the software was * developed by the University of Southern California, Information * Sciences Institute. The name of the University may not be used to * endorse or promote products derived from this software without * specific prior written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about * the suitability of this software for any purpose. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Other copyrights might apply to parts of this software and are so * noted when applicable. */ #ifndef __RSRR_VAR_H__ #define __RSRR_VAR_H__ /* RSRR things that are only needed by mrouted. */ /* Cache of Route Query messages, distinguished by source, * destination, and client addresses. Cache is flushed by RSRR client * -- it sends notification when an unwanted Route Reply is received. * Since this only happens during route changes, it is more likely * that the cache will be flushed when the kernel table entry is * deleted. */ struct rsrr_cache { struct rsrr_rq route_query; /* Cached Route Query */ struct sockaddr_un client_addr; /* Client address */ int client_length; /* Length of client */ struct rsrr_cache *next; /* next cache item */ }; #endif /* __RSRR_VAR_H__ */ pimd-2.3.2/timer.c000066400000000000000000000660671267035112600137550ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: timer.c,v 1.31 2001/09/10 20:31:37 pavlin Exp $ */ #include "defs.h" /* * Global variables */ /* To account for header overhead, we apx 1 byte/s = 10 bits/s (bps) * Note, in the new spt_threshold setting the rate is in kbps as well! */ spt_threshold_t spt_threshold = { .mode = SPT_THRESHOLD_DEFAULT_MODE, .bytes = SPT_THRESHOLD_DEFAULT_RATE * SPT_THRESHOLD_DEFAULT_INTERVAL / 10 * 1000, .packets = SPT_THRESHOLD_DEFAULT_PACKETS, .interval = SPT_THRESHOLD_DEFAULT_INTERVAL, }; /* * Local variables */ uint16_t unicast_routing_interval = UCAST_ROUTING_CHECK_INTERVAL; uint16_t unicast_routing_timer; /* Used to check periodically for any * change in the unicast routing. */ uint8_t ucast_flag; uint16_t pim_spt_threshold_timer; /* Used for periodic check of spt-threshold * for the RP or the lasthop router. */ uint8_t rate_flag; /* * TODO: XXX: the timers below are not used. Instead, the data rate timer is used. */ uint16_t kernel_cache_timer; /* Used to timeout the kernel cache * entries for idle sources */ uint16_t kernel_cache_interval; /* to request and compare any route changes */ srcentry_t srcentry_save; rpentry_t rpentry_save; /* * Init some timers */ void init_timers(void) { SET_TIMER(unicast_routing_timer, unicast_routing_interval); SET_TIMER(pim_spt_threshold_timer, spt_threshold.interval); /* Initialize the srcentry and rpentry used to save the old routes * during unicast routing change discovery process. */ srcentry_save.prev = NULL; srcentry_save.next = NULL; srcentry_save.address = INADDR_ANY_N; srcentry_save.mrtlink = NULL; srcentry_save.incoming = NO_VIF; srcentry_save.upstream = NULL; srcentry_save.metric = ~0; srcentry_save.preference = ~0; RESET_TIMER(srcentry_save.timer); srcentry_save.cand_rp = NULL; rpentry_save.prev = NULL; rpentry_save.next = NULL; rpentry_save.address = INADDR_ANY_N; rpentry_save.mrtlink = NULL; rpentry_save.incoming = NO_VIF; rpentry_save.upstream = NULL; rpentry_save.metric = ~0; rpentry_save.preference = ~0; RESET_TIMER(rpentry_save.timer); rpentry_save.cand_rp = NULL; } /* * On every timer interrupt, advance (i.e. decrease) the timer for each * neighbor and group entry for each vif. */ void age_vifs(void) { vifi_t vifi; struct uvif *v; pim_nbr_entry_t *next, *curr; /* XXX: TODO: currently, sending to qe* interface which is DOWN * doesn't return error (ENETDOWN) on my Solaris machine, * so have to check periodically the * interfaces status. If this is fixed, just remove the defs around * the "if (vifs_down)" line. */ #if (!((defined SunOS) && (SunOS >= 50))) if (vifs_down) #endif /* Solaris */ check_vif_state(); /* Age many things */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; /* Timeout neighbors */ for (curr = v->uv_pim_neighbors; curr; curr = next) { next = curr->next; /* Never timeout neighbors with holdtime = 0xffff. * This may be used with ISDN lines to avoid keeping the * link up with periodic Hello messages. */ /* TODO: XXX: TIMER implem. dependency! */ if (PIM_HELLO_HOLDTIME_FOREVER == curr->timer) continue; IF_NOT_TIMEOUT(curr->timer) continue; logit(LOG_INFO, 0, "Delete PIM neighbor %s on %s (holdtime timeout)", inet_fmt(curr->address, s2, sizeof(s2)), v->uv_name); delete_pim_nbr(curr); } /* PIM_HELLO periodic */ IF_TIMEOUT(v->uv_hello_timer) send_pim_hello(v, pim_timer_hello_holdtime); #ifdef TOBE_DELETED /* PIM_JOIN_PRUNE periodic */ /* TODO: XXX: TIMER implem. dependency! */ if (v->uv_jp_timer <= TIMER_INTERVAL) /* TODO: need to scan the whole routing table, * because different entries have different Join/Prune timer. * Probably don't need the Join/Prune timer per vif. */ send_pim_join_prune(vifi, NULL, PIM_JOIN_PRUNE_HOLDTIME); else /* TODO: XXX: TIMER implem. dependency! */ v->uv_jp_timer -= TIMER_INTERVAL; #endif /* TOBE_DELETED */ /* IGMP query periodic */ IF_TIMEOUT(v->uv_gq_timer) query_groups(v); if (v->uv_querier && (v->uv_querier->al_timer += TIMER_INTERVAL) > igmp_querier_timeout) { /* * The current querier has timed out. We must become the * querier. */ IF_DEBUG(DEBUG_IGMP) { logit(LOG_DEBUG, 0, "IGMP Querier %s timed out.", inet_fmt(v->uv_querier->al_addr, s1, sizeof(s1))); } free(v->uv_querier); v->uv_querier = NULL; v->uv_flags |= VIFF_QUERIER; query_groups(v); } } IF_DEBUG(DEBUG_IF) { fputs("\n", stderr); dump_vifs(stderr); } } #define MRT_IS_LASTHOP(mrt) VIFM_LASTHOP_ROUTER(mrt->leaves, mrt->oifs) #define MRT_IS_RP(mrt) mrt->incoming == reg_vif_num static void try_switch_to_spt(mrtentry_t *mrt, kernel_cache_t *kc) { if (MRT_IS_LASTHOP(mrt) || MRT_IS_RP(mrt)) { #ifdef KERNEL_MFC_WC_G if (kc->source == INADDR_ANY_N) { delete_single_kernel_cache(mrt, kc); mrt->flags |= MRTF_MFC_CLONE_SG; return; } #endif /* KERNEL_MFC_WC_G */ switch_shortest_path(kc->source, kc->group); } } /* * Check the SPT threshold for a given (*,*,RP) or (*,G) entry * * XXX: the spec says to start monitoring first the total traffic for * all senders for particular (*,*,RP) or (*,G) and if the total traffic * exceeds some predefined threshold, then start monitoring the data * traffic for each particular sender for this group: (*,G) or * (*,*,RP). However, because the kernel cache/traffic info is of the * form (S,G), it is easier if we are simply collecting (S,G) traffic * all the time. * * For (*,*,RP) if the number of bytes received between the last check * and now exceeds some precalculated value (based on interchecking * period and datarate threshold AND if there are directly connected * members (i.e. we are their last hop(e) router), then create (S,G) and * start initiating (S,G) Join toward the source. The same applies for * (*,G). The spec does not say that if the datarate goes below a given * threshold, then will switch back to the shared tree, hence after a * switch to the source-specific tree occurs, a source with low * datarate, but periodically sending will keep the (S,G) states. * * If a source with kernel cache entry has been idle after the last time * a check of the datarate for the whole routing table, then delete its * kernel cache entry. */ static void check_spt_threshold(mrtentry_t *mrt) { int status; uint32_t prev_bytecnt, prev_pktcnt; kernel_cache_t *kc, *kc_next; /* XXX: TODO: When we add group-list support to spt-threshold we need * to move this infinity check to inside the for-loop ... obviously. */ if (!rate_flag || spt_threshold.mode == SPT_INF) return; for (kc = mrt->kernel_cache; kc; kc = kc_next) { kc_next = kc->next; prev_bytecnt = kc->sg_count.bytecnt; prev_pktcnt = kc->sg_count.pktcnt; status = k_get_sg_cnt(udp_socket, kc->source, kc->group, &kc->sg_count); if (status || prev_bytecnt == kc->sg_count.bytecnt) { /* Either (for whatever reason) there is no such routing * entry, or that particular (S,G) was idle. Delete the * routing entry from the kernel. */ delete_single_kernel_cache(mrt, kc); continue; } // TODO: Why is this needed? try_switch_to_spt(mrt, kc); /* Check spt-threshold for forwarder and RP, should we switch to * source specific tree (SPT). Need to check only when we have * (S,G)RPbit in the forwarder or the RP itself. */ switch (spt_threshold.mode) { case SPT_RATE: if (prev_bytecnt + spt_threshold.bytes < kc->sg_count.bytecnt) try_switch_to_spt(mrt, kc); break; case SPT_PACKETS: if (prev_pktcnt + spt_threshold.packets < kc->sg_count.pktcnt) try_switch_to_spt(mrt, kc); break; default: ; /* INF not handled here yet. */ } /* XXX: currently the spec doesn't say to switch back to the * shared tree if low datarate, but if needed to implement, the * check must be done here. Don't forget to check whether I am a * forwarder for that source. */ } } /* * Scan the whole routing table and timeout a bunch of timers: * - oifs timers * - Join/Prune timer * - routing entry * - Assert timer * - Register-Suppression timer * * - If the global timer for checking the unicast routing has expired, perform * also iif/upstream router change verification * - If the global timer for checking the data rate has expired, check the * number of bytes forwarded after the lastest timeout. If bigger than * a given threshold, then switch to the shortest path. * If `number_of_bytes == 0`, then delete the kernel cache entry. * * Only the entries which have the Join/Prune timer expired are sent. * In the special case when we have ~(S,G)RPbit Prune entry, we must * include any (*,G) or (*,*,RP) XXX: ???? what and why? * * Below is a table which summarizes the segmantic rules. * * On the left side is "if A must be included in the J/P message". * On the top is "shall/must include B?" * "Y" means "MUST include" * "SY" means "SHOULD include" * "N" means "NO NEED to include" * (G is a group that matches to RP) * * -----------||-----------||----------- * || (*,*,RP) || (*,G) || (S,G) || * ||-----------||-----------||-----------|| * || J | P || J | P || J | P || * ==================================================|| * J || n/a | n/a || N | Y || N | Y || * (*,*,RP) -----------------------------------------|| * P || n/a | n/a || SY | N || SY | N || * ==================================================|| * J || N | N || n/a | n/a || N | Y || * (*,G) -----------------------------------------|| * P || N | N || n/a | n/a || SY | N || * ==================================================|| * J || N | N || N | N || n/a | n/a || * (S,G) -----------------------------------------|| * P || N | N || N | N || n/a | n/a || * ================================================== * */ void age_routes(void) { cand_rp_t *cand_rp; grpentry_t *grp; grpentry_t *grp_next; mrtentry_t *mrt_grp; mrtentry_t *mrt_rp; mrtentry_t *mrt_wide; mrtentry_t *mrt_srcs; mrtentry_t *mrt_srcs_next; rp_grp_entry_t *rp_grp; struct uvif *v; vifi_t vifi; pim_nbr_entry_t *nbr; int change_flag; int rp_action, grp_action, src_action = PIM_ACTION_NOTHING, src_action_rp = PIM_ACTION_NOTHING; int dont_calc_action; rpentry_t *rp; int update_rp_iif; int update_src_iif; vifbitmap_t new_pruned_oifs; int assert_timer_expired = 0; /* * Timing out of the global `unicast_routing_timer` * and `data_rate_timer` */ IF_TIMEOUT(unicast_routing_timer) { ucast_flag = TRUE; SET_TIMER(unicast_routing_timer, unicast_routing_interval); } ELSE { ucast_flag = FALSE; } IF_TIMEOUT(pim_spt_threshold_timer) { rate_flag = TRUE; SET_TIMER(pim_spt_threshold_timer, spt_threshold.interval); } ELSE { rate_flag = FALSE; } /* Scan the (*,*,RP) entries */ for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) { rp = cand_rp->rpentry; /* Need to save only `incoming` and `upstream` to discover * unicast route changes. `metric` and `preference` are not * interesting for us. */ rpentry_save.incoming = rp->incoming; rpentry_save.upstream = rp->upstream; update_rp_iif = FALSE; if ((ucast_flag == TRUE) && (rp->address != my_cand_rp_address)) { /* I am not the RP. If I was the RP, then the iif is * register_vif and no need to reset it. */ if (set_incoming(rp, PIM_IIF_RP) != TRUE) { /* TODO: XXX: no route to that RP. Panic? There is a high * probability the network is partitioning so immediately * remapping to other RP is not a good idea. Better wait * the Bootstrap mechanism to take care of it and provide * me with correct Cand-RP-Set. */ } else { if ((rpentry_save.upstream != rp->upstream) || (rpentry_save.incoming != rp->incoming)) { /* Routing change has occur. Update all (*,G) * and (S,G)RPbit iifs mapping to that RP */ update_rp_iif = TRUE; } } } rp_action = PIM_ACTION_NOTHING; mrt_rp = cand_rp->rpentry->mrtlink; if (mrt_rp) { /* outgoing interfaces timers */ change_flag = FALSE; for (vifi = 0; vifi < numvifs; vifi++) { if (VIFM_ISSET(vifi, mrt_rp->joined_oifs)) { IF_TIMEOUT(mrt_rp->vif_timers[vifi]) { VIFM_CLR(vifi, mrt_rp->joined_oifs); change_flag = TRUE; } } } if ((change_flag == TRUE) || (update_rp_iif == TRUE)) { change_interfaces(mrt_rp, rp->incoming, mrt_rp->joined_oifs, mrt_rp->pruned_oifs, mrt_rp->leaves, mrt_rp->asserted_oifs, 0); mrt_rp->upstream = rp->upstream; } /* Check the activity for this entry */ check_spt_threshold(mrt_rp); /* Join/Prune timer */ IF_TIMEOUT(mrt_rp->jp_timer) { rp_action = join_or_prune(mrt_rp, mrt_rp->upstream); if (rp_action != PIM_ACTION_NOTHING) add_jp_entry(mrt_rp->upstream, PIM_JOIN_PRUNE_HOLDTIME, htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, mrt_rp->source->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, rp_action); SET_TIMER(mrt_rp->jp_timer, PIM_JOIN_PRUNE_PERIOD); } /* Assert timer */ if (mrt_rp->flags & MRTF_ASSERTED) { IF_TIMEOUT(mrt_rp->assert_timer) { /* TODO: XXX: reset the upstream router now */ mrt_rp->flags &= ~MRTF_ASSERTED; } } /* Register-Suppression timer */ /* TODO: to reduce the kernel calls, if the timer is running, * install a negative cache entry in the kernel? */ /* TODO: can we have Register-Suppression timer for (*,*,RP)? * Currently no... */ IF_TIMEOUT(mrt_rp->rs_timer) {} /* routing entry */ if ((TIMEOUT(mrt_rp->timer)) && (VIFM_ISEMPTY(mrt_rp->leaves))) delete_mrtentry(mrt_rp); } /* if (mrt_rp) */ /* Just in case if that (*,*,RP) was deleted */ mrt_rp = cand_rp->rpentry->mrtlink; /* Check the (*,G) and (S,G) entries */ for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) { for (grp = rp_grp->grplink; grp; grp = grp_next) { grp_next = grp->rpnext; grp_action = PIM_ACTION_NOTHING; mrt_grp = grp->grp_route; mrt_srcs = grp->mrtlink; if (mrt_grp) { /* The (*,G) entry */ /* outgoing interfaces timers */ change_flag = FALSE; assert_timer_expired = 0; if (mrt_grp->flags & MRTF_ASSERTED) assert_timer_expired = TIMEOUT(mrt_grp->assert_timer); for (vifi = 0; vifi < numvifs; vifi++) { if (VIFM_ISSET(vifi, mrt_grp->joined_oifs)) { IF_TIMEOUT(mrt_grp->vif_timers[vifi]) { VIFM_CLR(vifi, mrt_grp->joined_oifs); change_flag = TRUE; } } if (assert_timer_expired) { VIFM_CLR(vifi, mrt_grp->asserted_oifs); change_flag = TRUE; mrt_grp->flags &= ~MRTF_ASSERTED; } } if ((change_flag == TRUE) || (update_rp_iif == TRUE)) { change_interfaces(mrt_grp, rp->incoming, mrt_grp->joined_oifs, mrt_grp->pruned_oifs, mrt_grp->leaves, mrt_grp->asserted_oifs, 0); mrt_grp->upstream = rp->upstream; } /* Check the sources activity */ check_spt_threshold(mrt_grp); dont_calc_action = FALSE; if (rp_action != PIM_ACTION_NOTHING) { dont_calc_action = TRUE; grp_action = join_or_prune(mrt_grp, mrt_grp->upstream); if (((rp_action == PIM_ACTION_JOIN) && (grp_action == PIM_ACTION_PRUNE)) || ((rp_action == PIM_ACTION_PRUNE) && (grp_action == PIM_ACTION_JOIN))) FIRE_TIMER(mrt_grp->jp_timer); } /* Join/Prune timer */ IF_TIMEOUT(mrt_grp->jp_timer) { if (dont_calc_action != TRUE) grp_action = join_or_prune(mrt_grp, mrt_grp->upstream); if (grp_action != PIM_ACTION_NOTHING) add_jp_entry(mrt_grp->upstream, PIM_JOIN_PRUNE_HOLDTIME, mrt_grp->group->group, SINGLE_GRP_MSKLEN, cand_rp->rpentry->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, grp_action); SET_TIMER(mrt_grp->jp_timer, PIM_JOIN_PRUNE_PERIOD); } /* Register-Suppression timer */ /* TODO: to reduce the kernel calls, if the timer * is running, install a negative cache entry in * the kernel? */ /* TODO: currently cannot have Register-Suppression * timer for (*,G) entry, but keep this around. */ IF_TIMEOUT(mrt_grp->rs_timer) {} /* routing entry */ if ((TIMEOUT(mrt_grp->timer)) && (VIFM_ISEMPTY(mrt_grp->leaves))) delete_mrtentry(mrt_grp); } /* if (mrt_grp) */ /* For all (S,G) for this group */ /* XXX: mrt_srcs was set before */ for (; mrt_srcs; mrt_srcs = mrt_srcs_next) { /* routing entry */ mrt_srcs_next = mrt_srcs->grpnext; /* outgoing interfaces timers */ change_flag = FALSE; assert_timer_expired = 0; if (mrt_srcs->flags & MRTF_ASSERTED) assert_timer_expired = TIMEOUT(mrt_srcs->assert_timer); for (vifi = 0; vifi < numvifs; vifi++) { if (VIFM_ISSET(vifi, mrt_srcs->joined_oifs)) { /* TODO: checking for reg_num_vif is slow! */ if (vifi != reg_vif_num) { IF_TIMEOUT(mrt_srcs->vif_timers[vifi]) { VIFM_CLR(vifi, mrt_srcs->joined_oifs); change_flag = TRUE; } } } if (assert_timer_expired) { VIFM_CLR(vifi, mrt_srcs->asserted_oifs); change_flag = TRUE; mrt_srcs->flags &= ~MRTF_ASSERTED; } } update_src_iif = FALSE; if (ucast_flag == TRUE) { if (!(mrt_srcs->flags & MRTF_RP)) { /* iif toward the source */ srcentry_save.incoming = mrt_srcs->source->incoming; srcentry_save.upstream = mrt_srcs->source->upstream; if (set_incoming(mrt_srcs->source, PIM_IIF_SOURCE) != TRUE) { /* XXX: not in the spec! * Cannot find route toward that source. * This is bad. Delete the entry. */ delete_mrtentry(mrt_srcs); continue; } /* iif info found */ if ((srcentry_save.incoming != mrt_srcs->incoming) || (srcentry_save.upstream != mrt_srcs->upstream)) { /* Route change has occur */ update_src_iif = TRUE; mrt_srcs->incoming = mrt_srcs->source->incoming; mrt_srcs->upstream = mrt_srcs->source->upstream; } } else { /* (S,G)RPBit with iif toward RP */ if ((rpentry_save.upstream != mrt_srcs->upstream) || (rpentry_save.incoming != mrt_srcs->incoming)) { update_src_iif = TRUE; /* XXX: a hack */ /* XXX: setup the iif now! */ mrt_srcs->incoming = rp->incoming; mrt_srcs->upstream = rp->upstream; } } } if ((change_flag == TRUE) || (update_src_iif == TRUE)) /* Flush the changes */ change_interfaces(mrt_srcs, mrt_srcs->incoming, mrt_srcs->joined_oifs, mrt_srcs->pruned_oifs, mrt_srcs->leaves, mrt_srcs->asserted_oifs, 0); check_spt_threshold(mrt_srcs); mrt_wide = mrt_srcs->group->grp_route; if (!mrt_wide) mrt_wide = mrt_rp; dont_calc_action = FALSE; if ((rp_action != PIM_ACTION_NOTHING) || (grp_action != PIM_ACTION_NOTHING)) { src_action_rp = join_or_prune(mrt_srcs, rp->upstream); src_action = src_action_rp; dont_calc_action = TRUE; if (src_action_rp == PIM_ACTION_JOIN) { if ((grp_action == PIM_ACTION_PRUNE) || (rp_action == PIM_ACTION_PRUNE)) FIRE_TIMER(mrt_srcs->jp_timer); } else if (src_action_rp == PIM_ACTION_PRUNE) { if ((grp_action == PIM_ACTION_JOIN) || (rp_action == PIM_ACTION_JOIN)) FIRE_TIMER(mrt_srcs->jp_timer); } } /* Join/Prune timer */ IF_TIMEOUT(mrt_srcs->jp_timer) { if ((dont_calc_action != TRUE) || (rp->upstream != mrt_srcs->upstream)) src_action = join_or_prune(mrt_srcs, mrt_srcs->upstream); if (src_action != PIM_ACTION_NOTHING) add_jp_entry(mrt_srcs->upstream, PIM_JOIN_PRUNE_HOLDTIME, mrt_srcs->group->group, SINGLE_GRP_MSKLEN, mrt_srcs->source->address, SINGLE_SRC_MSKLEN, mrt_srcs->flags & MRTF_RP, src_action); if (mrt_wide) { /* Have both (S,G) and (*,G) (or (*,*,RP)). * Check if need to send (S,G) PRUNE toward RP */ if (mrt_srcs->upstream != mrt_wide->upstream) { if (dont_calc_action != TRUE) src_action_rp = join_or_prune(mrt_srcs, mrt_wide->upstream); /* XXX: TODO: do error check if * src_action == PIM_ACTION_JOIN, which * should be an error. */ if (src_action_rp == PIM_ACTION_PRUNE) add_jp_entry(mrt_wide->upstream, PIM_JOIN_PRUNE_HOLDTIME, mrt_srcs->group->group, SINGLE_GRP_MSKLEN, mrt_srcs->source->address, SINGLE_SRC_MSKLEN, MRTF_RP, src_action_rp); } } SET_TIMER(mrt_srcs->jp_timer, PIM_JOIN_PRUNE_PERIOD); } /* Register-Suppression timer */ /* TODO: to reduce the kernel calls, if the timer * is running, install a negative cache entry in * the kernel? */ IF_TIMER_SET(mrt_srcs->rs_timer) { IF_TIMEOUT(mrt_srcs->rs_timer) { /* Start encapsulating the packets */ VIFM_COPY(mrt_srcs->pruned_oifs, new_pruned_oifs); VIFM_CLR(reg_vif_num, new_pruned_oifs); change_interfaces(mrt_srcs, mrt_srcs->incoming, mrt_srcs->joined_oifs, new_pruned_oifs, mrt_srcs->leaves, mrt_srcs->asserted_oifs, 0); } ELSE { /* The register suppression timer is running. Check * whether it is time to send PIM_NULL_REGISTER. */ /* TODO: XXX: TIMER implem. dependency! */ if (mrt_srcs->rs_timer <= PIM_REGISTER_PROBE_TIME) /* Time to send a PIM_NULL_REGISTER */ /* XXX: a (bad) hack! This will be sending * periodically NULL_REGISTERS between * PIM_REGISTER_PROBE_TIME and 0. Well, * because PROBE_TIME is 5 secs, it will * happen only once, so it helps to avoid * adding a flag to the routing entry whether * a NULL_REGISTER was sent. */ send_pim_null_register(mrt_srcs); } } /* routing entry */ if (TIMEOUT(mrt_srcs->timer)) { if (VIFM_ISEMPTY(mrt_srcs->leaves)) { delete_mrtentry(mrt_srcs); continue; } /* XXX: if DR, Register suppressed, * and leaf oif inherited from (*,G), the * directly connected source is not active anymore, * this (S,G) entry won't timeout. Check if the leaf * oifs are inherited from (*,G); if true. delete the * (S,G) entry. */ if (mrt_srcs->group->grp_route) { if (!((mrt_srcs->group->grp_route->leaves & mrt_srcs->leaves) ^ mrt_srcs->leaves)) { delete_mrtentry(mrt_srcs); continue; } } } } /* End of (S,G) loop */ } /* End of (*,G) loop */ } } /* For all cand RPs */ /* TODO: check again! */ for (vifi = 0, v = &uvifs[0]; vifi < numvifs; vifi++, v++) { /* Send all pending Join/Prune messages */ for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) pack_and_send_jp_message(nbr); } IF_DEBUG(DEBUG_PIM_MRT) { fputs("\n", stderr); dump_pim_mrt(stderr); } } /* * TODO: timeout the RP-group mapping entries during the scan of the * whole routing table? */ void age_misc(void) { rp_grp_entry_t *rp; rp_grp_entry_t *rp_next; grp_mask_t *grp; grp_mask_t *grp_next; /* Timeout the Cand-RP-set entries */ for (grp = grp_mask_list; grp; grp = grp_next) { /* If we timeout an entry, the grp entry might be removed */ grp_next = grp->next; for (rp = grp->grp_rp_next; rp; rp = rp_next) { rp_next = rp->grp_rp_next; if (rp->holdtime < 60000) { IF_TIMEOUT(rp->holdtime) { if (rp->group!=NULL) { logit(LOG_INFO, 0, "Delete RP group entry for group %s (holdtime timeout)", inet_fmt(rp->group->group_addr, s2, sizeof(s2))); } delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, rp); } } } } /* Cand-RP-Adv timer */ if (cand_rp_flag == TRUE) { IF_TIMEOUT(pim_cand_rp_adv_timer) { send_pim_cand_rp_adv(); SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period); } } /* bootstrap-timer */ IF_TIMEOUT(pim_bootstrap_timer) { if (cand_bsr_flag == FALSE) { /* If I am not Cand-BSR, start accepting Bootstrap messages from anyone. * XXX: Even if the BSR has timeout, the existing Cand-RP-Set is kept. */ SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT); curr_bsr_fragment_tag = 0; curr_bsr_priority = 0; /* Lowest priority */ curr_bsr_address = INADDR_ANY_N; /* Lowest priority */ MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, curr_bsr_hash_mask); } else { /* I am Cand-BSR, so set the current BSR to me */ if (curr_bsr_address == my_bsr_address) { SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_PERIOD); send_pim_bootstrap(); } else { /* Short delay before becoming the BSR and start sending * of the Cand-RP set (to reduce the transient control * overhead). */ SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay()); curr_bsr_fragment_tag = RANDOM(); curr_bsr_priority = my_bsr_priority; curr_bsr_address = my_bsr_address; curr_bsr_hash_mask = my_bsr_hash_mask; } } } IF_DEBUG(DEBUG_PIM_BOOTSTRAP | DEBUG_PIM_CAND_RP) dump_rp_set(stderr); /* TODO: XXX: anything else to timeout */ } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/trace.c000066400000000000000000000434461267035112600137270ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: trace.c,v 1.13 2002/09/26 00:59:30 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" #include "trace.h" /* TODO: XXX: implementation incompleted and buggy; check Kame's pim6sd */ /* * Traceroute function which returns traceroute replies to the requesting * router. Also forwards the request to downstream routers. */ void accept_mtrace(uint32_t src, uint32_t dst, uint32_t group, char *data, u_int no, int datalen) { uint8_t type; mrtentry_t *mrt; struct tr_query *qry; struct tr_resp *resp; int vifi; char *p; u_int rcount; int errcode = TR_NO_ERR; int resptype; struct timeval tp; struct sioc_vif_req v_req; #if 0 /* TODO */ struct sioc_sg_req sg_req; #endif /* 0 */ uint32_t parent_address = INADDR_ANY; /* Remember qid across invocations */ static uint32_t oqid = 0; /* timestamp the request/response */ gettimeofday(&tp, 0); /* * Check if it is a query or a response */ if (datalen == QLEN) { type = QUERY; IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } } else if ((datalen - QLEN) % RLEN == 0) { type = RESP; IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } if (IN_MULTICAST(ntohl(dst))) { IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Dropping multicast response"); } return; } } else { logit(LOG_WARNING, 0, "%s from %s to %s", "Non decipherable traceroute request received", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return; } qry = (struct tr_query *)data; /* * if it is a packet with all reports filled, drop it */ if ((rcount = (datalen - QLEN)/RLEN) == no) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "packet with all reports filled in"); return; } IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(qry->tr_dst, s3, sizeof(s3))); logit(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl, inet_fmt(qry->tr_raddr, s1, sizeof(s1))); logit(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid); } /* determine the routing table entry for this traceroute */ mrt = find_route(qry->tr_src, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); IF_DEBUG(DEBUG_TRACE) { if (mrt != (mrtentry_t *)NULL) { if (mrt->upstream != (pim_nbr_entry_t *)NULL) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; logit(LOG_DEBUG, 0, "mrt parent vif: %d rtr: %s metric: %d", mrt->incoming, inet_fmt(parent_address, s1, sizeof(s1)), mrt->metric); /* TODO logit(LOG_DEBUG, 0, "mrt origin %s", RT_FMT(rt, s1)); */ } else { logit(LOG_DEBUG, 0, "...no route"); } } /* * Query type packet - check if rte exists * Check if the query destination is a vif connected to me. * and if so, whether I should start response back */ if (type == QUERY) { if (oqid == qry->tr_qid) { /* * If the multicast router is a member of the group being * queried, and the query is multicasted, then the router can * recieve multiple copies of the same query. If we have already * replied to this traceroute, just ignore it this time. * * This is not a total solution, but since if this fails you * only get N copies, N <= the number of interfaces on the router, * it is not fatal. */ IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "ignoring duplicate traceroute packet"); } return; } if (!mrt) { IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s", inet_fmt(qry->tr_src, s1, sizeof(s1))); } if (IN_MULTICAST(ntohl(dst))) return; } vifi = find_vif_direct(qry->tr_dst); if (vifi == NO_VIF) { /* The traceroute destination is not on one of my subnet vifs. */ IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Destination %s not an interface", inet_fmt(qry->tr_dst, s1, sizeof(s1))); } if (IN_MULTICAST(ntohl(dst))) return; errcode = TR_WRONG_IF; } else if (mrt && !VIFM_ISSET(vifi, mrt->oifs)) { IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1, sizeof(s1)), inet_fmt(qry->tr_src, s2, sizeof(s2))); } if (IN_MULTICAST(ntohl(dst))) return; errcode = TR_WRONG_IF; } } else { /* * determine which interface the packet came in on * RESP packets travel hop-by-hop so this either traversed * a tunnel or came from a directly attached mrouter. */ vifi = find_vif_direct(src); if (vifi == NO_VIF) { IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Wrong interface for packet"); } errcode = TR_WRONG_IF; } } /* Now that we've decided to send a response, save the qid */ oqid = qry->tr_qid; IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Sending traceroute response"); } /* copy the packet to the sending buffer */ p = igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN; bcopy(data, p, datalen); p += datalen; /* * If there is no room to insert our reply, coopt the previous hop * error indication to relay this fact. */ if (p + sizeof(struct tr_resp) > igmp_send_buf + SEND_BUF_SIZE) { resp = (struct tr_resp *)p - 1; resp->tr_rflags = TR_NO_SPACE; mrt = NULL; goto sendit; } /* * fill in initial response fields */ resp = (struct tr_resp *)p; memset(resp, 0, sizeof(struct tr_resp)); datalen += RLEN; resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) + ((tp.tv_usec << 10) / 15625)); resp->tr_rproto = PROTO_PIM; resp->tr_outaddr = (vifi == NO_VIF) ? dst : uvifs[vifi].uv_lcl_addr; resp->tr_fttl = (vifi == NO_VIF) ? 0 : uvifs[vifi].uv_threshold; resp->tr_rflags = errcode; /* * obtain # of packets out on interface */ v_req.vifi = vifi; if (vifi != NO_VIF && ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifout = htonl(v_req.ocount); else resp->tr_vifout = 0xffffffff; /* * fill in scoping & pruning information */ /* TODO */ #if 0 if (mrt) { for (gt = rt->rt_groups; gt; gt = gt->gt_next) { if (gt->gt_mcastgrp >= group) break; } } else { gt = NULL; } if (gt && gt->gt_mcastgrp == group) { struct stable *st; for (st = gt->gt_srctbl; st; st = st->st_next) { if (qry->tr_src == st->st_origin) break; } sg_req.src.s_addr = qry->tr_src; sg_req.grp.s_addr = group; if (st && st->st_ctime != 0 && ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0) resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt); else resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff); if (VIFM_ISSET(vifi, gt->gt_scope)) { resp->tr_rflags = TR_SCOPED; } else if (gt->gt_prsent_timer) { resp->tr_rflags = TR_PRUNED; } else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) { if (VIFM_ISSET(vifi, rt->rt_children) && NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap, rt->rt_subordinates)) /*XXX*/ resp->tr_rflags = TR_OPRUNED; else resp->tr_rflags = TR_NO_FWD; } } else { if (scoped_addr(vifi, group)) resp->tr_rflags = TR_SCOPED; else if (rt && !VIFM_ISSET(vifi, rt->rt_children)) resp->tr_rflags = TR_NO_FWD; } #endif /* 0 */ /* * if no rte exists, set NO_RTE error */ if (!mrt) { src = dst; /* the dst address of resp. pkt */ resp->tr_inaddr = 0; resp->tr_rflags = TR_NO_RTE; resp->tr_rmtaddr = 0; } else { /* get # of packets in on interface */ v_req.vifi = mrt->incoming; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifin = htonl(v_req.icount); else resp->tr_vifin = 0xffffffff; /* TODO MASK_TO_VAL(rt->rt_originmask, resp->tr_smask); */ src = uvifs[mrt->incoming].uv_lcl_addr; resp->tr_inaddr = src; if (mrt->upstream) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; resp->tr_rmtaddr = parent_address; if (vifi != NO_VIF && !VIFM_ISSET(vifi, mrt->oifs)) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1, sizeof(s1)), inet_fmt(qry->tr_src, s2, sizeof(s2))); resp->tr_rflags = TR_WRONG_IF; } #if 0 if (rt->rt_metric >= UNREACHABLE) { resp->tr_rflags = TR_NO_RTE; /* Hack to send reply directly */ rt = NULL; } #endif /* 0 */ } sendit: /* * if metric is 1 or no. of reports is 1, send response to requestor * else send to upstream router. If the upstream router can't handle * mtrace, set an error code and send to requestor anyway. */ IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no); if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) { resptype = IGMP_MTRACE_RESP; dst = qry->tr_raddr; } else { #if 0 /* TODO */ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) { dst = qry->tr_raddr; resp->tr_rflags = TR_OLD_ROUTER; resptype = IGMP_MTRACE_RESP; } else { #endif /* 0 */ if (mrt->upstream) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; dst = parent_address; resptype = IGMP_MTRACE; #if 0 /* TODO */ } #endif } if (IN_MULTICAST(ntohl(dst))) { /* * Send the reply on a known multicast capable vif. * If we don't have one, we can't source any multicasts anyway. */ if (phys_vif != -1) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Sending reply to %s from %s", inet_fmt(dst, s1, sizeof(s1)), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2, sizeof(s2))); k_set_ttl(igmp_socket, qry->tr_rttl); send_igmp(igmp_send_buf, uvifs[phys_vif].uv_lcl_addr, dst, resptype, no, group, datalen); k_set_ttl(igmp_socket, 1); } else { logit(LOG_INFO, 0, "No enabled phyints -- dropping traceroute reply"); } } else { IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "Sending %s to %s from %s", resptype == IGMP_MTRACE_RESP ? "reply" : "request on", inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2))); } send_igmp(igmp_send_buf, src, dst, resptype, no, group, datalen); } } /* * accept_neighbor_request() supports some old DVMRP messages from mrinfo. * Haven't tested it, because I have only the new mrinfo. */ void accept_neighbor_request(uint32_t src, uint32_t dst __attribute__((unused))) { vifi_t vifi; struct uvif *v; uint8_t *p, *ncount; /* struct listaddr *la; */ pim_nbr_entry_t *pim_nbr; int datalen; uint32_t temp_addr, them = src; #define PUT_ADDR(a) temp_addr = ntohl(a); \ *p++ = temp_addr >> 24; \ *p++ = (temp_addr >> 16) & 0xFF; \ *p++ = (temp_addr >> 8) & 0xFF; \ *p++ = temp_addr & 0xFF; p = (uint8_t *) (igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { if (v->uv_flags & VIFF_DISABLED) continue; ncount = 0; /* TODO: XXX: if we are PMBR, then check the DVMRP interfaces too */ for (pim_nbr = v->uv_pim_neighbors; pim_nbr != (pim_nbr_entry_t *)NULL; pim_nbr = pim_nbr->next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(igmp_send_buf, INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(PIMD_LEVEL), datalen); p = (uint8_t *) (igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { PUT_ADDR(v->uv_lcl_addr); *p++ = v->uv_metric; *p++ = v->uv_threshold; ncount = p; *p++ = 0; datalen += 4 + 3; } PUT_ADDR(pim_nbr->address); datalen += 4; (*ncount)++; } } if (datalen != 0) send_igmp(igmp_send_buf, INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(PIMD_LEVEL), datalen); } /* * Send a list of all of our neighbors to the requestor, `src'. * Used for mrinfo support. * XXX: currently, we cannot specify the used multicast routing protocol; * only a protocol version is returned. */ void accept_neighbor_request2(uint32_t src, uint32_t dst __attribute__((unused))) { vifi_t vifi; struct uvif *v; uint8_t *p, *ncount; /* struct listaddr *la; */ pim_nbr_entry_t *pim_nbr; int datalen; uint32_t them = src; p = (uint8_t *) (igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN); datalen = 0; for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) { uint32_t vflags = v->uv_flags; uint8_t rflags = 0; if (vflags & VIFF_TUNNEL) rflags |= DVMRP_NF_TUNNEL; if (vflags & VIFF_SRCRT) rflags |= DVMRP_NF_SRCRT; if (vflags & VIFF_PIM_NBR) rflags |= DVMRP_NF_PIM; if (vflags & VIFF_DOWN) rflags |= DVMRP_NF_DOWN; if (vflags & VIFF_DISABLED) rflags |= DVMRP_NF_DISABLED; if (vflags & VIFF_QUERIER) rflags |= DVMRP_NF_QUERIER; if (vflags & VIFF_LEAF) rflags |= DVMRP_NF_LEAF; ncount = 0; pim_nbr = v->uv_pim_neighbors; if (pim_nbr == (pim_nbr_entry_t *)NULL) { /* * include down & disabled interfaces and interfaces on * leaf nets. */ if (rflags & DVMRP_NF_TUNNEL) rflags |= DVMRP_NF_DOWN; if (datalen > MAX_DVMRP_DATA_LEN - 12) { send_igmp(igmp_send_buf, INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(PIMD_LEVEL), datalen); p = (uint8_t *) (igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN); datalen = 0; } *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; *p++ = 1; *(u_int*)p = v->uv_rmt_addr; p += 4; datalen += 12; } else { for ( ; pim_nbr; pim_nbr = pim_nbr->next) { /* Make sure that there's room for this neighbor... */ if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) { send_igmp(igmp_send_buf, INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(PIMD_LEVEL), datalen); p = (uint8_t *) (igmp_send_buf + IP_IGMP_HEADER_LEN + IGMP_MINLEN); datalen = 0; ncount = 0; } /* Put out the header for this neighbor list... */ if (ncount == 0) { *(u_int*)p = v->uv_lcl_addr; p += 4; *p++ = v->uv_metric; *p++ = v->uv_threshold; *p++ = rflags; ncount = p; *p++ = 0; datalen += 4 + 4; } *(u_int*)p = pim_nbr->address; p += 4; datalen += 4; (*ncount)++; } } } if (datalen != 0) send_igmp(igmp_send_buf, INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(PIMD_LEVEL), datalen); } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/trace.h000066400000000000000000000120411267035112600137170ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: trace.h,v 1.7 2001/09/10 20:31:37 pavlin Exp $ */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ /* * The packet format for a traceroute request. */ struct tr_query { uint32_t tr_src; /* traceroute source */ uint32_t tr_dst; /* traceroute destination */ uint32_t tr_raddr; /* traceroute response address */ #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) struct { u_int qid : 24; /* traceroute query id */ u_int ttl : 8; /* traceroute response ttl */ } q; #else struct { u_int ttl : 8; /* traceroute response ttl */ u_int qid : 24; /* traceroute query id */ } q; #endif /* BYTE_ORDER */ }; #define tr_rttl q.ttl #define tr_qid q.qid /* * Traceroute response format. A traceroute response has a tr_query at the * beginning, followed by one tr_resp for each hop taken. */ struct tr_resp { uint32_t tr_qarr; /* query arrival time */ uint32_t tr_inaddr; /* incoming interface address */ uint32_t tr_outaddr; /* outgoing interface address */ uint32_t tr_rmtaddr; /* parent address in source tree */ uint32_t tr_vifin; /* input packet count on interface */ uint32_t tr_vifout; /* output packet count on interface */ uint32_t tr_pktcnt; /* total incoming packets for src-grp */ uint8_t tr_rproto; /* routing protocol deployed on router */ uint8_t tr_fttl; /* ttl required to forward on outvif */ uint8_t tr_smask; /* subnet mask for src addr */ uint8_t tr_rflags; /* forwarding error codes */ }; /* defs within mtrace */ #define QUERY 1 #define RESP 2 #define QLEN sizeof(struct tr_query) #define RLEN sizeof(struct tr_resp) /* fields for tr_rflags (forwarding error codes) */ #define TR_NO_ERR 0 /* No error */ #define TR_WRONG_IF 1 /* traceroute arrived on non-oif */ #define TR_PRUNED 2 /* router has sent a prune upstream */ #define TR_OPRUNED 3 /* stop forw. after request from next hop rtr*/ #define TR_SCOPED 4 /* group adm. scoped at this hop */ #define TR_NO_RTE 5 /* no route for the source */ #define TR_NO_LHR 6 /* not the last-hop router */ #define TR_NO_FWD 7 /* not forwarding for this (S,G). Reason = ? */ #define TR_RP 8 /* I am the RP/Core */ #define TR_IIF 9 /* request arrived on the iif */ #define TR_NO_MULTI 0x0a /* multicast disabled on that interface */ #define TR_NO_SPACE 0x81 /* no space to insert responce data block */ #define TR_OLD_ROUTER 0x82 /* previous hop does not support traceroute */ #define TR_ADMIN_PROHIB 0x83 /* traceroute adm. prohibited */ /* fields for tr_smask */ #define TR_GROUP_ONLY 0x2f /* forwarding solely on group state */ #define TR_SUBNET_COUNT 0x40 /* pkt count for (S,G) is for source network */ /* fields for packets count */ #define TR_CANT_COUNT 0xffffffff /* no count can be reported */ /* fields for tr_rproto (routing protocol) */ #define PROTO_DVMRP 1 #define PROTO_MOSPF 2 #define PROTO_PIM 3 #define PROTO_CBT 4 #define PROTO_PIM_SPECIAL 5 #define PROTO_PIM_STATIC 6 #define PROTO_DVMRP_STATIC 7 #define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv) /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/vif.c000066400000000000000000000477441267035112600134220ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ #include "defs.h" /* * Helper macros */ #define is_uv_subnet(src, v) \ (src & v->uv_subnetmask) == v->uv_subnet && ((v->uv_subnetmask == 0xffffffff) || (src != v->uv_subnetbcast)) #define is_pa_subnet(src, v) \ (src & p->pa_subnetmask) == p->pa_subnet && ((p->pa_subnetmask == 0xffffffff) || (src != p->pa_subnetbcast)) /* * Exported variables. */ struct uvif uvifs[MAXVIFS]; /* array of all virtual interfaces */ vifi_t numvifs; /* Number of vifs in use */ int vifs_down; /* 1=>some interfaces are down */ int phys_vif; /* An enabled vif */ vifi_t reg_vif_num; /* really virtual interface for registers */ int udp_socket; /* Since the honkin' kernel doesn't support * ioctls on raw IP sockets, we need a UDP * socket as well as our IGMP (raw) socket. */ int total_interfaces; /* Number of all interfaces: including the * non-configured, but excluding the * loopback interface and the non-multicast * capable interfaces. */ uint32_t default_route_metric = UCAST_DEFAULT_ROUTE_METRIC; uint32_t default_route_distance = UCAST_DEFAULT_ROUTE_DISTANCE; /* * Forward declarations */ static void start_vif (vifi_t vifi); static void stop_vif (vifi_t vifi); static void start_all_vifs (void); static int init_reg_vif (void); static int update_reg_vif (vifi_t register_vifi); void init_vifs(void) { vifi_t vifi; struct uvif *v; int enabled_vifs; numvifs = 0; reg_vif_num = NO_VIF; vifs_down = FALSE; /* Configure the vifs based on the interface configuration of the the kernel and * the contents of the configuration file. (Open a UDP socket for ioctl use in * the config procedures if the kernel can't handle IOCTL's on the IGMP socket.) */ #ifdef IOCTL_OK_ON_RAW_SOCKET udp_socket = igmp_socket; #else if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) logit(LOG_ERR, errno, "UDP socket"); #endif /* Clean up all vifs */ for (vifi = 0, v = uvifs; vifi < MAXVIFS; ++vifi, ++v) zero_vif(v, FALSE); logit(LOG_INFO, 0, "Getting vifs from kernel"); config_vifs_from_kernel(); if (disable_all_by_default) { logit(LOG_INFO, 0, "Disabling all vifs from kernel"); for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) v->uv_flags |= VIFF_DISABLED; } logit(LOG_INFO, 0, "Getting vifs from %s", config_file); config_vifs_from_file(); /* * Quit if there are fewer than two enabled vifs. */ enabled_vifs = 0; phys_vif = -1; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* Initialize the outgoing timeout for each vif. Currently use a fixed time * 'PIM_JOIN_PRUNE_HOLDTIME'. Later, may add a configurable array to feed * these parameters, or compute them as function of the i/f bandwidth and the * overall connectivity...etc. */ SET_TIMER(v->uv_jp_timer, PIM_JOIN_PRUNE_HOLDTIME); if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_TUNNEL)) continue; if (phys_vif == -1) phys_vif = vifi; enabled_vifs++; } if (enabled_vifs < 1) /* XXX: TODO: */ logit(LOG_ERR, 0, "Cannot forward: %s", enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif"); k_init_pim(igmp_socket); /* Call to kernel to initialize structures */ /* Add a dummy virtual interface to support Registers in the kernel. * In order for this to work, the kernel has to have been modified * with the PIM patches to ip_mroute.{c,h} and ip.c */ init_reg_vif(); start_all_vifs(); } /* * Initialize the passed vif with all appropriate default values. * "t" is true if a tunnel or register_vif, or false if a phyint. */ void zero_vif(struct uvif *v, int t) { v->uv_flags = 0; /* Default to IGMPv3 */ v->uv_metric = DEFAULT_METRIC; v->uv_admetric = 0; v->uv_threshold = DEFAULT_THRESHOLD; v->uv_rate_limit = t ? DEFAULT_REG_RATE_LIMIT : DEFAULT_PHY_RATE_LIMIT; v->uv_lcl_addr = INADDR_ANY_N; v->uv_rmt_addr = INADDR_ANY_N; v->uv_dst_addr = t ? INADDR_ANY_N : allpimrouters_group; v->uv_subnet = INADDR_ANY_N; v->uv_subnetmask = INADDR_ANY_N; v->uv_subnetbcast = INADDR_ANY_N; strlcpy(v->uv_name, "", IFNAMSIZ); v->uv_groups = (struct listaddr *)NULL; v->uv_dvmrp_neighbors = (struct listaddr *)NULL; NBRM_CLRALL(v->uv_nbrmap); v->uv_querier = (struct listaddr *)NULL; v->uv_igmpv1_warn = 0; v->uv_prune_lifetime = 0; v->uv_acl = (struct vif_acl *)NULL; RESET_TIMER(v->uv_leaf_timer); v->uv_addrs = (struct phaddr *)NULL; v->uv_filter = (struct vif_filter *)NULL; RESET_TIMER(v->uv_hello_timer); v->uv_dr_prio = PIM_HELLO_DR_PRIO_DEFAULT; v->uv_genid = 0; RESET_TIMER(v->uv_gq_timer); RESET_TIMER(v->uv_jp_timer); v->uv_pim_neighbors = (struct pim_nbr_entry *)NULL; v->uv_local_pref = default_route_distance; v->uv_local_metric = default_route_metric; #ifdef __linux__ v->uv_ifindex = -1; #endif /* __linux__ */ } /* * Add a (the) register vif to the vif table. */ static int init_reg_vif(void) { struct uvif *v; vifi_t i; v = &uvifs[numvifs]; v->uv_flags = 0; if ((numvifs + 1) == MAXVIFS) { /* Exit the program! The PIM router must have a Register vif */ logit(LOG_ERR, 0, "Cannot install the Register vif: too many interfaces"); return FALSE; } /* * So far in PIM we need only one register vif and we save its number in * the global reg_vif_num. */ reg_vif_num = numvifs; /* set the REGISTER flag */ v->uv_flags = VIFF_REGISTER; #ifdef PIM_EXPERIMENTAL v->uv_flags |= VIFF_REGISTER_KERNEL_ENCAP; #endif strlcpy(v->uv_name, "register_vif0", sizeof(v->uv_name)); /* Use the address of the first available physical interface to * create the register vif. */ for (i = 0; i < numvifs; i++) { if (uvifs[i].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER | VIFF_TUNNEL)) continue; break; } if (i >= numvifs) { logit(LOG_ERR, 0, "No physical interface enabled"); return -1; } v->uv_lcl_addr = uvifs[i].uv_lcl_addr; v->uv_threshold = MINTTL; numvifs++; total_interfaces++; return 0; } static void start_all_vifs(void) { vifi_t vifi; struct uvif *v; u_int action; /* Start first the NON-REGISTER vifs */ for (action = 0; ; action = VIFF_REGISTER) { for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* If starting non-registers but the vif is a register or if starting * registers, but the interface is not a register, then just continue. */ if ((v->uv_flags & VIFF_REGISTER) ^ action) continue; /* Start vif if not DISABLED or DOWN */ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN)) { if (v->uv_flags & VIFF_DISABLED) logit(LOG_INFO, 0, "Interface %s is DISABLED; vif #%u out of service", v->uv_name, vifi); else logit(LOG_INFO, 0, "Interface %s is DOWN; vif #%u out of service", v->uv_name, vifi); } else { start_vif(vifi); } } if (action == VIFF_REGISTER) break; /* We are done */ } } /* * stop all vifs */ void stop_all_vifs(void) { vifi_t vifi; struct uvif *v; for (vifi = 0; vifi < numvifs; vifi++) { v = &uvifs[vifi]; if (!(v->uv_flags & VIFF_DOWN)) stop_vif(vifi); } } /* * Initialize the vif and add to the kernel. The vif can be either * physical, register or tunnel (tunnels will be used in the future * when this code becomes PIM multicast boarder router. */ static void start_vif(vifi_t vifi) { struct uvif *v; v = &uvifs[vifi]; /* Initialy no router on any vif */ if (v->uv_flags & VIFF_REGISTER) v->uv_flags = v->uv_flags & ~VIFF_DOWN; else { v->uv_flags = (v->uv_flags | VIFF_DR | VIFF_NONBRS) & ~VIFF_DOWN; /* https://tools.ietf.org/html/draft-ietf-pim-hello-genid-01 */ v->uv_genid = RANDOM(); SET_TIMER(v->uv_hello_timer, 1 + RANDOM() % pim_timer_hello_interval); SET_TIMER(v->uv_jp_timer, 1 + RANDOM() % PIM_JOIN_PRUNE_PERIOD); /* TODO: CHECK THE TIMERS!!!!! Set or reset? */ RESET_TIMER(v->uv_gq_timer); v->uv_pim_neighbors = (pim_nbr_entry_t *)NULL; } /* Tell kernel to add, i.e. start this vif */ k_add_vif(igmp_socket, vifi, &uvifs[vifi]); logit(LOG_INFO, 0, "Interface %s comes up; vif #%u now in service", v->uv_name, vifi); if (!(v->uv_flags & VIFF_REGISTER)) { /* Join the PIM multicast group on the interface. */ k_join(pim_socket, allpimrouters_group, v); /* Join the ALL-ROUTERS multicast group on the interface. This * allows mtrace requests to loop back if they are run on the * multicast router. */ k_join(igmp_socket, allrouters_group, v); /* Join INADDR_ALLRPTS_GROUP to support IGMPv3 membership reports */ k_join(igmp_socket, allreports_group, v); /* Until neighbors are discovered, assume responsibility for sending * periodic group membership queries to the subnet. Send the first * query. */ v->uv_flags |= VIFF_QUERIER; query_groups(v); /* Send a probe via the new vif to look for neighbors. */ send_pim_hello(v, pim_timer_hello_holdtime); } #ifdef __linux__ else { struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); if (mrt_table_id != 0) { logit(LOG_INFO, 0, "Initializing pimreg%u", mrt_table_id); snprintf(ifr.ifr_name, IFNAMSIZ, "pimreg%u", mrt_table_id); } else { strlcpy(ifr.ifr_name, "pimreg", IFNAMSIZ); } if (ioctl(udp_socket, SIOGIFINDEX, (char *) &ifr) < 0) { logit(LOG_ERR, errno, "ioctl SIOGIFINDEX for %s", ifr.ifr_name); /* Not reached */ return; } v->uv_ifindex = ifr.ifr_ifindex; } #endif /* __linux__ */ } /* * Stop a vif (either physical interface, tunnel or * register.) If we are running only PIM we don't have tunnels. */ static void stop_vif(vifi_t vifi) { struct uvif *v; struct listaddr *a, *b; pim_nbr_entry_t *n, *next; struct vif_acl *acl; /* * TODO: make sure that the kernel viftable is * consistent with the daemon table */ v = &uvifs[vifi]; if (!(v->uv_flags & VIFF_REGISTER)) { k_leave(pim_socket, allpimrouters_group, v); k_leave(igmp_socket, allrouters_group, v); k_leave(igmp_socket, allreports_group, v); /* Discard all group addresses. (No need to tell kernel; * the k_del_vif() call will clean up kernel state.) */ while (v->uv_groups) { a = v->uv_groups; v->uv_groups = a->al_next; while (a->al_sources) { b = a->al_sources; a->al_sources = a->al_next; free(b); } free(a); } } /* * TODO: inform (eventually) the neighbors I am going down by sending * PIM_HELLO with holdtime=0 so someone else should become a DR. */ /* TODO: dummy! Implement it!! Any problems if don't use it? */ delete_vif_from_mrt(vifi); /* Delete the interface from the kernel's vif structure. */ k_del_vif(igmp_socket, vifi, v); v->uv_flags = (v->uv_flags & ~VIFF_DR & ~VIFF_QUERIER & ~VIFF_NONBRS) | VIFF_DOWN; if (!(v->uv_flags & VIFF_REGISTER)) { RESET_TIMER(v->uv_hello_timer); RESET_TIMER(v->uv_jp_timer); RESET_TIMER(v->uv_gq_timer); for (n = v->uv_pim_neighbors; n; n = next) { next = n->next; /* Free the space for each neighbour */ delete_pim_nbr(n); } v->uv_pim_neighbors = NULL; } /* TODO: currently not used */ /* The Access Control List (list with the scoped addresses) */ while (v->uv_acl) { acl = v->uv_acl; v->uv_acl = acl->acl_next; free(acl); } vifs_down = TRUE; logit(LOG_INFO, 0, "Interface %s goes down; vif #%u out of service", v->uv_name, vifi); } /* * Update the register vif in the multicast routing daemon and the * kernel because the interface used initially to get its local address * is DOWN. register_vifi is the index to the Register vif which needs * to be updated. As a result the Register vif has a new uv_lcl_addr and * is UP (virtually :)) */ static int update_reg_vif(vifi_t register_vifi) { struct uvif *v; vifi_t vifi; /* Find the first useable vif with solid physical background */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_TUNNEL)) continue; /* Found. Stop the bogus Register vif first */ stop_vif(register_vifi); uvifs[register_vifi].uv_lcl_addr = uvifs[vifi].uv_lcl_addr; start_vif(register_vifi); IF_DEBUG(DEBUG_PIM_REGISTER | DEBUG_IF) { logit(LOG_NOTICE, 0, "Interface %s has come up; vif #%u now in service", uvifs[register_vifi].uv_name, register_vifi); } return 0; } vifs_down = TRUE; logit(LOG_WARNING, 0, "Cannot start Register vif: %s", uvifs[vifi].uv_name); return -1; } /* * See if any interfaces have changed from up state to down, or vice versa, * including any non-multicast-capable interfaces that are in use as local * tunnel end-points. Ignore interfaces that have been administratively * disabled. */ void check_vif_state(void) { vifi_t vifi; struct uvif *v; struct ifreq ifr; static int checking_vifs = 0; /* * XXX: TODO: True only for DVMRP?? Check. * If we get an error while checking, (e.g. two interfaces go down * at once, and we decide to send a prune out one of the failed ones) * then don't go into an infinite loop! */ if (checking_vifs) return; vifs_down = FALSE; checking_vifs = 1; /* TODO: Check all potential interfaces!!! */ /* Check the physical and tunnels only */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & (VIFF_DISABLED | VIFF_REGISTER)) continue; /* get the interface flags */ strlcpy(ifr.ifr_name, v->uv_name, sizeof(ifr.ifr_name)); if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0) { if (errno == ENODEV) { logit(LOG_NOTICE, 0, "Interface %s has gone; vif #%u taken out of service", v->uv_name, vifi); stop_vif(vifi); vifs_down = TRUE; continue; } logit(LOG_ERR, errno, "check_vif_state: ioctl SIOCGIFFLAGS for %s", ifr.ifr_name); } if (v->uv_flags & VIFF_DOWN) { if (ifr.ifr_flags & IFF_UP) start_vif(vifi); else vifs_down = TRUE; } else { if (!(ifr.ifr_flags & IFF_UP)) { logit(LOG_NOTICE, 0, "Interface %s has gone down; vif #%u taken out of service", v->uv_name, vifi); stop_vif(vifi); vifs_down = TRUE; } } } /* Check the register(s) vif(s) */ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { vifi_t vifi2; struct uvif *v2; int found; if (!(v->uv_flags & VIFF_REGISTER)) continue; found = 0; /* Find a physical vif with the same IP address as the * Register vif. */ for (vifi2 = 0, v2 = uvifs; vifi2 < numvifs; ++vifi2, ++v2) { if (v2->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_TUNNEL)) continue; if (v->uv_lcl_addr != v2->uv_lcl_addr) continue; found = 1; break; } /* The physical interface with the IP address as the Register * vif is probably DOWN. Get a replacement. */ if (!found) update_reg_vif(vifi); } checking_vifs = 0; } /* * If the source is directly connected to us, find the vif number for * the corresponding physical interface (Register and tunnels excluded). * Local addresses are excluded. * Return the vif number or NO_VIF if not found. */ vifi_t find_vif_direct(uint32_t src) { vifi_t vifi; struct uvif *v; struct phaddr *p; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_TUNNEL)) continue; if (src == v->uv_lcl_addr) return NO_VIF; /* src is one of our IP addresses */ if (is_uv_subnet(src, v)) return vifi; /* Check the extra subnets for this vif */ /* TODO: don't think currently pimd can handle extra subnets */ for (p = v->uv_addrs; p; p = p->pa_next) { if (is_pa_subnet(src, v)) return vifi; } /* POINTOPOINT but not VIFF_TUNNEL interface (e.g., GRE) */ if ((v->uv_flags & VIFF_POINT_TO_POINT) && (src == v->uv_rmt_addr)) return vifi; } return NO_VIF; } /* * Checks if src is local address. If "yes" return the vif index, * otherwise return value is NO_VIF. */ vifi_t local_address(uint32_t src) { vifi_t vifi; struct uvif *v; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* TODO: XXX: what about VIFF_TUNNEL? */ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; if (src == v->uv_lcl_addr) return vifi; } /* Returning NO_VIF means not a local address */ return NO_VIF; } /* * If the source is directly connected, or is local address, * find the vif number for the corresponding physical interface * (Register and tunnels excluded). * Return the vif number or NO_VIF if not found. */ vifi_t find_vif_direct_local(uint32_t src) { vifi_t vifi; struct uvif *v; struct phaddr *p; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* TODO: XXX: what about VIFF_TUNNEL? */ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_TUNNEL)) continue; if (src == v->uv_lcl_addr) return vifi; /* src is one of our IP addresses */ if (is_uv_subnet(src, v)) return vifi; /* Check the extra subnets for this vif */ /* TODO: don't think currently pimd can handle extra subnets */ for (p = v->uv_addrs; p; p = p->pa_next) { if (is_pa_subnet(src, v)) return vifi; } /* POINTOPOINT but not VIFF_TUNNEL interface (e.g., GRE) */ if ((v->uv_flags & VIFF_POINT_TO_POINT) && (src == v->uv_rmt_addr)) return vifi; } return NO_VIF; } /* * Returns the highest address of local vif that is UP and ENABLED. * The VIFF_REGISTER interface(s) is/are excluded. */ uint32_t max_local_address(void) { vifi_t vifi; struct uvif *v; uint32_t max_address = 0; for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) { /* Count vif if not DISABLED or DOWN */ /* TODO: XXX: What about VIFF_TUNNEL? */ if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; if (ntohl(v->uv_lcl_addr) > ntohl(max_address)) max_address = v->uv_lcl_addr; } return max_address; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pimd-2.3.2/vif.h000066400000000000000000000277431267035112600134240ustar00rootroot00000000000000/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * */ /* * Bitmap handling functions. * These should be fast but generic. bytes can be slow to zero and compare, * words are hard to make generic. Thus two sets of macros (yuk). */ /* * The VIFM_ functions should migrate out of , since * the kernel no longer uses vifbitmaps. */ #ifndef VIFM_SET typedef uint32_t vifbitmap_t; #define VIFM_SET(n, m) ((m) |= (1 << (n))) #define VIFM_CLR(n, m) ((m) &= ~(1 << (n))) #define VIFM_ISSET(n, m) ((m) & (1 << (n))) #define VIFM_CLRALL(m) ((m) = 0x00000000) #define VIFM_COPY(mfrom, mto) ((mto) = (mfrom)) #define VIFM_SAME(m1, m2) ((m1) == (m2)) #endif /* * And was missing some required functions anyway */ #if !defined(VIFM_SETALL) #define VIFM_SETALL(m) ((m) = ~0) #endif #define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n))) #define VIFM_ISEMPTY(m) ((m) == 0) #define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask)) #define VIFM_SET_MASK(m, mask) ((m) |= (mask)) #define VIFM_MERGE(m1, m2, result) ((result) = (m1) | (m2)) /* Check whether I am the last hop on some LAN */ #define VIFM_LASTHOP_ROUTER(leaves, oifs) ((leaves) & (oifs)) /* * Neighbor bitmaps are, for efficiency, implemented as a struct * containing two variables of a native machine type. If you * have a native type that's bigger than a long, define it below. */ #define NBRTYPE uint32_t #define NBRBITS sizeof(NBRTYPE) * 8 typedef struct { NBRTYPE hi; NBRTYPE lo; } nbrbitmap_t; #define MAXNBRS 2 * NBRBITS #define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \ ((m).hi |= (1 << (n - NBRBITS)))) #define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \ ((m).hi &= ~(1 << (n - NBRBITS)))) #define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \ ((m).hi & (1 << ((n) - NBRBITS)))) #define NBRM_CLRALL(m) ((m).lo = (m).hi = 0) #define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi) #define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi)) #define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0)) #define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi)) #define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi)) #define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi)) #define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi)) #define NBRM_ISSETALLMASK(m, mask)\ ((((m).lo & (mask).lo) == (mask).lo) && \ (((m).hi & (mask).hi) == (mask).hi)) /* * This macro is TRUE if all the subordinates have been pruned, or if * there are no subordinates on this vif. * The arguments is the map of subordinates, the map of neighbors on the * vif, and the map of received prunes. */ #define SUBS_ARE_PRUNED(sub, vifmask, prunes) \ (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \ ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi)) /* * User level Virtual Interface structure * * A "virtual interface" is either a physical, multicast-capable interface * (called a "phyint"), a virtual point-to-point link (called a "tunnel") * or a "register vif" used by PIM. The register vif is used by the * Designated Router (DR) to send encapsulated data packets to the * Rendevous Point (RP) for a particular group. The data packets are * encapsulated in PIM messages (IPPROTO_PIM = 103) and then unicast to * the RP. * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.) */ struct uvif { u_int uv_flags; /* VIFF_ flags defined below */ uint8_t uv_metric; /* cost of this vif */ uint8_t uv_admetric; /* advertised cost of this vif */ uint8_t uv_threshold; /* min ttl required to forward on vif */ u_int uv_rate_limit; /* rate limit on this vif */ int uv_mtu; /* Initially interface MTU, then PMTU */ uint32_t uv_lcl_addr; /* local address of this vif */ uint32_t uv_rmt_addr; /* remote end-point addr (tunnels only) */ uint32_t uv_dst_addr; /* destination for DVMRP/PIM messages */ uint32_t uv_subnet; /* subnet number (phyints only) */ uint32_t uv_subnetmask; /* subnet mask (phyints only) */ uint32_t uv_subnetbcast;/* subnet broadcast addr (phyints only) */ char uv_name[IFNAMSIZ]; /* interface name */ struct listaddr *uv_groups; /* list of local groups (phyints only) */ struct listaddr *uv_dvmrp_neighbors; /* list of neighboring routers */ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */ struct listaddr *uv_querier; /* IGMP querier on vif */ int uv_igmpv1_warn;/* To rate-limit IGMPv1 warnings */ int uv_prune_lifetime; /* Prune lifetime or 0 for default */ struct vif_acl *uv_acl; /* access control list of groups */ int uv_leaf_timer; /* time until this vif is considrd leaf */ struct phaddr *uv_addrs; /* Additional subnets on this vif */ struct vif_filter *uv_filter; /* Route filters on this vif */ uint16_t uv_hello_timer; /* Timer for sending PIM hello msgs */ uint32_t uv_dr_prio; /* PIM Hello DR Priority */ uint32_t uv_genid; /* Random PIM Hello Generation ID */ uint16_t uv_gq_timer; /* Group Query timer */ uint16_t uv_jp_timer; /* The Join/Prune timer */ int uv_local_pref; /* default local preference for assert */ int uv_local_metric;/* default local metric for assert */ struct pim_nbr_entry *uv_pim_neighbors; /* list of PIM neighbor routers */ #ifdef __linux__ int uv_ifindex; /* because RTNETLINK returns only index */ #endif /* __linux__ */ }; /* TODO: define VIFF_KERNEL_FLAGS */ #define VIFF_KERNEL_FLAGS (VIFF_TUNNEL | VIFF_SRCRT) #define VIFF_DOWN 0x000100 /* kernel state of interface */ #define VIFF_DISABLED 0x000200 /* administratively disabled */ #define VIFF_QUERIER 0x000400 /* I am the subnet's querier */ #define VIFF_ONEWAY 0x000800 /* Maybe one way interface */ #define VIFF_LEAF 0x001000 /* all neighbors are leaves */ #define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */ #define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */ #define VIFF_PASSIVE 0x008000 /* passive tunnel */ #define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */ #define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */ #define VIFF_DR 0x040000 /* designated router */ /* TODO: VIFF_NONBRS == VIFF_ONEWAY? */ #define VIFF_NONBRS 0x080000 /* no neighbor on vif */ #define VIFF_POINT_TO_POINT 0x100000 /* point-to-point link */ #define VIFF_PIM_NBR 0x200000 /* PIM neighbor */ #define VIFF_DVMRP_NBR 0x400000 /* DVMRP neighbor */ #define VIFF_IGMPV2 0x800000 /* Act as an IGMPv2 Router */ struct phaddr { struct phaddr *pa_next; uint32_t pa_subnet; /* extra subnet */ uint32_t pa_subnetmask; /* netmask of extra subnet */ uint32_t pa_subnetbcast; /* broadcast of extra subnet */ }; /* The Access Control List (list with scoped addresses) member */ struct vif_acl { struct vif_acl *acl_next; /* next acl member */ uint32_t acl_addr; /* Group address */ uint32_t acl_mask; /* Group addr. mask */ }; struct vif_filter { int vf_type; #define VFT_ACCEPT 1 #define VFT_DENY 2 int vf_flags; #define VFF_BIDIR 1 struct vf_element *vf_filter; }; struct vf_element { struct vf_element *vfe_next; uint32_t vfe_addr; uint32_t vfe_mask; int vfe_flags; #define VFEF_EXACT 0x0001 }; struct listaddr { struct listaddr *al_next; /* link to next addr, MUST BE FIRST */ uint32_t al_addr; /* local group or neighbor address */ struct listaddr *al_sources; /* link to sources */ uint32_t al_timer; /* for timing out group or neighbor */ time_t al_ctime; /* entry creation time */ union { uint32_t alu_genid; /* generation id for neighbor */ uint32_t alu_reporter; /* a host which reported membership */ } al_alu; uint8_t al_pv; /* router protocol version */ uint8_t al_mv; /* router mrouted version */ uint8_t al_old; /* time since heard old report */ uint8_t al_index; /* neighbor index */ uint32_t al_timerid; /* timer for group membership */ uint32_t al_query; /* timer for repeated leave query */ uint16_t al_flags; /* flags related to this neighbor */ u_long al_versiontimer; /* timer for version switch */ }; #define al_genid al_alu.alu_genid #define al_reporter al_alu.alu_reporter #define NBRF_LEAF 0x0001 /* This neighbor is a leaf */ #define NBRF_GENID 0x0100 /* I know this neighbor's genid */ #define NBRF_WAITING 0x0200 /* Waiting for peering to come up */ #define NBRF_ONEWAY 0x0400 /* One-way peering */ #define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */ #define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */ #define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */ /* * Don't peer with neighbors with any of these flags set */ #define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \ NBRF_TOOMANYROUTES|NBRF_NOTPRUNING) #define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */ /* * Used to get the RPF neighbor and IIF info * for a given source from the unicast routing table. */ struct rpfctl { struct in_addr source; /* the source for which we want iif and rpfnbr */ struct in_addr rpfneighbor;/* next hop towards the source */ vifi_t iif; /* the incoming interface to reach the next hop */ }; /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */ pax_global_header00006660000000000000000000000064126506530360014520gustar00rootroot0000000000000052 comment=f9caffc9b71c1bd92aa4b520179473e8e84f9e7f pimd-2.3.2/libite/000077500000000000000000000000001265065303600137255ustar00rootroot00000000000000pimd-2.3.2/libite/.gitignore000066400000000000000000000002731265065303600157170ustar00rootroot00000000000000*~ *.d *.o *.so *.so.* *.a .deps .libs tmp/ compile config.* configure depcomp install-sh libtool ltmain.sh missing stamp-h1 Makefile Makefile.in aclocal.m4 ar-lib autom4te.cache libite* pimd-2.3.2/libite/.travis.yml000066400000000000000000000006241265065303600160400ustar00rootroot00000000000000# Travis CI integration -- https://travis-ci.org/troglobit/libuev # Defaults to GNU GCC and autotools: ./configure && make && make test language: c # Some of the autotests may use sudo, so use non-dockerized build atm. # http://stackoverflow.com/questions/26299552/travis-sudo-is-disabled sudo: true # - make test script: - ./autogen.sh - ./configure --disable-silent-rules - make -j5 clean all pimd-2.3.2/libite/ChangeLog000077700000000000000000000000001265065303600176432ChangeLog.mdustar00rootroot00000000000000pimd-2.3.2/libite/ChangeLog.md000066400000000000000000000072001265065303600160750ustar00rootroot00000000000000Change Log ========== All notable changes to the project are documented in this file. [v1.4.2][] - 2016-01-23 ----------------------- ### Fixes - Fix issue #2: GCC6 `-Wmisleading-indentation` causing FTBFS in pimd, which enables `-Werror` [v1.4.1][] - 2016-01-22 ----------------------- ### Fixes - Fix install path for include files, regression in v1.4.0 With the introduction of GNU autotools include file namespace was unfortunately lost. All include files were therefore installed in $(prefix)/include instead of $(prefix)/include/libite. [v1.4.0][] - 2016-01-22 ----------------------- ### Changes - Change to GNU configure and build system - Add Niels Provos' splay and red-black tree implementation from OpenBSD `sys/tree.h`, r1.14 - Make `pidfile()` file name accessible as `__pidfile_name` - Step ABI revision to 2.1, with help from http://250bpm.com/blog:41 [v1.3.0][] - 2016-01-07 ----------------------- ### Changes - Add highly useful typeless `min()`/`max()` macros to `lite.h`, courtesy of Tobias Waldekranz - Add BSD `queue.h` and Borland inspired `conio.h` to `install-dev` target. Needed by [Finit][]. - Note, this release update `queue.h` to OpenBSD v1.43, which removes support for circular queues (CIRCLEQ). - Update `strlcpy.c` to OpenBSD v1.12, readability fixes. - Update `strlcat.c` to OpenBSD v1.14, readability fixes. - Update `strtonum.c` to OpenBSD v1.7, tiniest of style tweaks. ### Fixes - Only update mtime if `pidfile()` is called more than once, do not install multiple `atexit()` handlers. - Add `#ifndef pidfile`, as for other BSD functions, in case the user already have a local copy, or a C library with one. [v1.2.0][] - 2015-11-23 ----------------------- ### Changes - Add simple parser for UNIX configuration files, e.g. `/etc/group`, `/etc/passwd`, and `/etc/protocols`: - `lfopen()` - `lfclose()` - `lftok()` - `lfgetkey()` - `lfgetint()` - `fgetint()` ### Fixes - Do not allow `VERSION` to be overloaded by build system. - Make sure we don't inherit `LDFLAGS` from environment. [v1.1.1][] - 2015-09-27 ----------------------- ### Fixes * Silence annoying warnings from GNU ar * Fix macro definitions for compat APIs [v1.1.0][] - 2015-09-16 ----------------------- ### Changes - Support for `make distdir` used by automake based projects - `lite.h` move from `error.h` to more BSD friendly `err.h` - Add highly useful `NELEMS()` and bitmanip macros - Lots of autotests added - Add support for running autotests from Travis - Add `progress()` simple progress bar - Add `tree()` simple command line tree replacement - Bump `SONAME` to `libite.so.2` on behalf of [Finit][] ### Fixes - `pidfile()` upgrade to OpenBSD r1.11 - Lots of README updates, fixes and additions [v1.0.0][] - 2015-07-12 ----------------------- Initial extraction of frog DNA from [Finit][]. See [README][] for API details. [UNRELEASED]: https://github.com/troglobit/libite/compare/v1.4.2...HEAD [v1.4.2]: https://github.com/troglobit/libite/compare/v1.4.1...v1.4.2 [v1.4.1]: https://github.com/troglobit/libite/compare/v1.4.0...v1.4.1 [v1.4.0]: https://github.com/troglobit/libite/compare/v1.3.0...v1.4.0 [v1.3.0]: https://github.com/troglobit/libite/compare/v1.2.0...v1.3.0 [v1.2.0]: https://github.com/troglobit/libite/compare/v1.1.1...v1.2.0 [v1.1.1]: https://github.com/troglobit/libite/compare/v1.1.0...v1.1.1 [v1.1.0]: https://github.com/troglobit/libite/compare/v1.0.0...v1.1.0 [v1.0.0]: https://github.com/troglobit/libite/compare/TAIL...v1.0.0 [Finit]: https://github.com/troglobit/finit/ [README]: https://github.com/troglobit/libite/blob/v1.0.0/README.md pimd-2.3.2/libite/LICENSE000066400000000000000000000026431265065303600147370ustar00rootroot00000000000000Much of the code in libite (-lite) is written by Claudio Matsuoka for Finit and released under the MIT/X11 license. Joachim Nilsson later improved on the Finit code base and included pieces of software released under the ISC and BSD licenses. See each respective file for license details. Copyright (c) 2008-2010 Claudio Matsuoka Copyright (c) 2008-2016 Joachim Nilsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pimd-2.3.2/libite/Makefile.am000066400000000000000000000030141265065303600157570ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libite.la pkginclude_HEADERS = lite.h queue.h conio.h tree.h libite_la_SOURCES = chomp.c copyfile.c dir.c fexist.c fisdir.c fmode.c fsendfile.c \ ifconfig.c lfile.c makepath.c pidfile.c pidfilefn.c progress.c \ rsync.c strlcpy.c strlcat.c strtonum.c tempfile.c tree.c libite_la_CPPFLAGS = -D_GNU_SOURCE libite_la_CFLAGS = -W -Wall -Wextra libite_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2:1:0 doc_DATA = README LICENSE #EXTRA_DIST = README.md AUTHORS LICENSE ChangeLog.md ## Generate MD5 checksum file MD5 = md5sum md5-dist: @for file in $(DIST_ARCHIVES); do \ $(MD5) $$file > $$file.md5; \ done ## Check if tagged in git release-hook: if [ ! `git tag | grep $(PACKAGE_VERSION)` ]; then \ echo; \ printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ printf "OK, aborting release.\n"; \ exit 1; \ fi; \ echo; \ else \ echo; \ printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ echo; \ fi ## Target to run when building a release release: dist release-hook md5-dist @for file in $(DIST_ARCHIVES); do \ printf "$$file \tDistribution tarball\n"; \ printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \ mv $$file* ../; \ done pimd-2.3.2/libite/README000077700000000000000000000000001265065303600160572README.mdustar00rootroot00000000000000pimd-2.3.2/libite/README.md000066400000000000000000000205251265065303600152100ustar00rootroot00000000000000-lite | Frog DNA, basically =========================== [![Travis Status][]][Travis] Libite is a lightweight library of *frog DNA*. It can be used to fill the gaps in any dinosaur project. It holds useful functions and macros developed by both [Finit][1] and the [OpenBSD][2] project. Most notably the string functions: [strlcpy(3)][3], [strlcat(3)][3] and the highly useful *BSD [sys/queue.h][4] and [sys/tree.h][7] API's. Libite aims to fill in the gaps missing in GLIBC/EGLIBC. (It does not aimo to become another [GLIB][5] though.) One such gap in GLIBC is the missing `_SAFE` macros in `sys/queue.h` — highly recommended when traversing lists to delete/free nodes. The code is open sourced under a mix of the [MIT/X11 license][MIT], the [ISC license][ISC] used by OpenBSD, and [BSD licenses][BSD], all of them are extremely liberal and can be used freely in proprietary software if needed. For an introduction to why Libite happened, and how you can use it, see [this blog post][6]. Helper Macros ------------- - `atonum(str)` Convert string to natural number, works for 32-bit non-negative integers. Returns -1 on error. (Uses `strtonum()` internally.) - `blkdev(dev)` Create block device - `chardev(dev)` Create character device - `erase(path)` Erase file/directory with `remove()`. Errors on stderr - `makedir(path)` Create directory, like `mkdir()`. Errors on stderr - `makefifo(path)` Create a FIFO, like `mkfifo()`. Errors on stderr - `min(a,b)`/`max(a,b)` These macros take care to avoid double evaluation. - `touch(path)` Create a file, or update mtime. Errors on stderr - `S_ISEXEC(mode_t m)` Mysteriously missing from GLIBC - `ISCLR(word,bit)` Is bit in (integer) word cleared? - `ISSET(word,bit)` Is bit in (integer) word set? - `ISOTHER(word,bit)` Is any other bits, except bit, in (integer) word set? - `SETBIT(word,bit)` Set bit in (integer) word. - `CLRBIT(word,bit)` Clear bit in (integer) word. - `NELEMS(array)` Returns the number of elements in an array. From the great book, The Practice of Programming, by Kernighan and Pike. - `UNUSED(var)` Shorter and more readable version of `var __attribute__ ((unused))` Generic Functions ----------------- - `chomp(str)` Perl like chomp function, chop off last char if newline. - `copyfile(src, dst, len, symlink)` Like the shell `cp(1)` and `dd(1),` can also create symlinks. - `dir(dir, ext, filter, list, strip)` Wrapper for `scandir()` with optional filter. Returns a list of names: files and directories that must be freed after use. See the unit test at the bottom for an example. - `fcopyfile(src, dst)` Like `copyfile()` but uses already open `FILE *` pointers. Copies from current file positions to current file positions until EOF. - `fexist(file)` Check for the existance of a file, returns True(1) or False(0). - `fisdir(path)` Check for the existance of a directory, returns True(1) or False(0). - `fmode(file)` Returns the `mode_t` bits of a file or directory. - `fsendfile(src, dst, len)` Copy data between file streams, very similar to `fcopyfile()`, but `dst` is allowed to be `NULL` to be able to read and discard `len` bytes from `src`. - `ifconfig(ifname, addr, mask, up)` Basic ifconfig like operations on an interface. Only supports IPv4 adresses. Note that mask is not CIDR notation. - `lfopen(file, sep)`, `lfclose(lf)` API for parsing UNIX style configuration files like `/etc/protocols` and `/etc/services`. - `lftok(lf)` Read tokens, delimeted by `sep`, from file opened with `lfopen()`. - `lfgetkey(lf, key)` Find `key` in file opened with `lfopen()`, return value/argument. - `lfgetint(lf, key)` Wrapper for `lfgetkey()`, returns positive integer value to `key`, or `-1` if `key` is not found. - `fgetint(file, sep, key)` Wrapper for `lfopen()`, `lfgetint()`, and `lfclose()`. Useful for when only reading a single `key` from a file. - `makepath(dir)` Create all components of the specified directory. - `mkpath(dir, mode)` Like `makepath()`, but also takes a `mode_t` permission mode argument. - `movefile(src, dst)` Like `copyfile()`, but renames `src` to `dst`, or recreates symlink with the `dst` name. On successful operation the source is removed and the function returns POSIX OK (0). - `pidfile(basename)` Create a daemon PID file in `_PATH_VARRUN` using either `basename` or, if `basename` is `NULL`, `__progname`. The resulting file name is available to the user as a read-only pointer: extern char *__pidfile_name; Use this function to create a PID file for your daemon when it is ready to receive signals. A client application may poll for the existence of this file, so make sure to have your signal handlers properly setup before calling this function. The pidfile is removed when the program exits, using an `atexit()` handler. However, depending on how the program terminates the file may still exist even though the program is no longer running. This is only a problem for clients. Calling this function multiple times updates the mtime of the file. Only one `atexit()` handler is created, regardless of the amount of times the function is called. See below for link to OpenBSD man page. - `pidfile_read(pidfile)` Read PID from pid file created by `pidfile()`. - `pidfile_signal(pidfile, signal)` Send signal to PID found in pid file created by `pidfile()`. - `progress(percent, max_width)` Simple ASCII progress bar with a spinner. Start it with `percent=0` and set the `max_width=chars` to indicate width of the progress bar. Called multiple times with the same percentage value cause spinner to spin. - `rsync(src, dst, delete, *filter())` Very simple `rsync()` to copy files files and directories recursively. - `tempfile()` Secure replacement for `tmpfile()`. Creates an invisible temporary file in `/tmp` that is removed when the returned `FILE` pointer is closed. **Note:** Requires Linux v3.11, or later, will fall back to the old and unsafe `tmpfile()` on older systems. - `tree(path, show_perms)` Very simple `/bin/tree` replacement. Draw ASCII tree of `path`, with optional listing of file and directory permissions if `show_perms` is set. The `path` argument should be a directory. OpenBSD Functions ----------------- The following are popular OpenBSD functions and highly useful macros. - `pidfile(basename)` http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/pidfile.3 **Note:** this version of `pidfile()` has been extended to handle it being called multiple times, and also to export the path to the PID file `__pidfile_name`, similar to `__progname`. See previous section for details. - `strlcpy(dst, src, len)` http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/strlcpy.3 - `strlcat(dst, src, len)` http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/strlcat.3 - `strtonum()` http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/strtonum.3 - `sys/queue.h` API http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/LIST_EMPTY.3 - `sys/tree.h` API Niels Provos' famous splay and red-black tree implementation. http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/SPLAY_FOREACH.3 Build & Install --------------- The library is built for and developed on GNU/Linux systems as a light weight utility library. Libite (LITE) employs the de facto standard GNU configure and build system: ./configure make all make install TODO ---- - Improve documentation, possibly use kdoc or gdoc2 to generate docs from API. - Continuously, update OpenBSD functions/macros. [1]: https://github.com/troglobit/finit [2]: http://www.openbsd.org/ [3]: http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy [4]: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/LIST_EMPTY.3 [5]: https://developer.gnome.org/glib/ [6]: http://troglobit.com/blog/2015/07/02/howto-using-lite-with-a-git-based-application/ [7]: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/SPLAY_FOREACH.3 [MIT]: https://en.wikipedia.org/wiki/MIT_License [ISC]: https://en.wikipedia.org/wiki/ISC_license [BSD]: https://en.wikipedia.org/wiki/BSD_licenses [Travis]: https://travis-ci.org/troglobit/libite [Travis Status]: https://travis-ci.org/troglobit/libite.png?branch=master pimd-2.3.2/libite/autogen.sh000077500000000000000000000000541265065303600157250ustar00rootroot00000000000000#!/bin/sh autoreconf -W portability -visfm pimd-2.3.2/libite/chomp.c000066400000000000000000000033701265065303600152020ustar00rootroot00000000000000/* Perl inspired chomp() implementation. * * Copyright (c) 2014-2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /** * chomp - Perl like chomp function, chop off last char(s) if newline. * @str: String to chomp * * This function is like Perl chomp, but it's set to chop of all * trailing newlines. * * Returns: * If @str is a valid pointer this function returns @str, otherwise * @errno is set to %EINVAL and this function returns %NULL. */ char *chomp(char *str) { char *p; if (!str || strlen(str) < 1) { errno = EINVAL; return NULL; } p = str + strlen(str) - 1; while (*p == '\n') *p-- = 0; return str; } #ifdef UNITTEST #include int main(void) { int i; char t[][16] = { "hej\ndej", "Slime\n\n\\n", "Tripple\n\n\n", "" }; for (i = 0; t[i][0]; i++) printf("[%02d]: '%s'\n", i, chomp(t[i])); return 0; } #endif /** * Local Variables: * compile-command: "make V=1 -f chomp.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/chomp.mk000066400000000000000000000001001265065303600153530ustar00rootroot00000000000000OBJS = TARGET = chomp.test clean include rules.mk pimd-2.3.2/libite/configure.ac000066400000000000000000000006111265065303600162110ustar00rootroot00000000000000# Use v2.61 for backwards compat with Ubuntu 12.04 LTS AC_PREREQ([2.61]) AC_INIT(libite, 1.4.2, https://github.com/troglobit/libite/issues) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR(lite.h) AM_CONFIG_HEADER(config.h) AC_CONFIG_FILES([Makefile]) AC_CONFIG_MACRO_DIR([m4]) AC_PROG_CC AM_PROG_AR LT_INIT AC_HEADER_STDC AC_C_CONST AC_OUTPUT pimd-2.3.2/libite/conio.h000066400000000000000000000055301265065303600152100ustar00rootroot00000000000000/* A conio.h like implementation for VTANSI displays. * * Copyright (c) 2009-2013 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef CONIO_H_ #define CONIO_H_ #include /* Attributes */ #define RESETATTR 0 #define BRIGHT 1 #define DIM 2 #define UNDERSCORE 4 #define BLINK 5 /* May not work on all displays. */ #define REVERSE 7 #define HIDDEN 8 /* Colors for text and background */ #define BLACK 0x0 #define RED 0x1 #define GREEN 0x2 #define BROWN 0x3 #define BLUE 0x4 #define MAGENTA 0x5 #define CYAN 0x6 #define LIGHTGREY 0x7 #define DARKGREY 0x10 #define LIGHTRED 0x11 #define LIGHTGREEN 0x12 #define YELLOW 0x13 #define LIGHTBLUE 0x14 #define LIGHTMAGENTA 0x15 #define LIGHTCYAN 0x16 #define WHITE 0x17 /* Esc[2JEsc[1;1H - Clear screen and move cursor to 1,1 (upper left) pos. */ #define clrscr() fputs ("\e[2J\e[1;1H", stdout) /* Esc[K - Erases from the current cursor position to the end of the current line. */ #define clreol() fputs ("\e[K", stdout) /* Esc[2K - Erases the entire current line. */ #define delline() fputs ("\e[2K", stdout) /* Esc[Line;ColumnH - Moves the cursor to the specified position (coordinates) */ #define gotoxy(x,y) printf("\e[%d;%dH", y, x) /* Esc[?25l (lower case L) - Hide Cursor */ #define hidecursor() fputs ("\e[?25l", stdout) /* Esc[?25h (lower case H) - Show Cursor */ #define showcursor() fputs ("\e[?25h", stdout) /* Esc[Value;...;Valuem - Set Graphics Mode */ #define __set_gm(attr,color,val) \ if (!color) \ printf("\e[%dm", attr); \ else \ printf("\e[%d;%dm", color & 0x10 ? 1 : 0, (color & 0xF) + val) #define textattr(attr) __set_gm(attr, 0, 0) #define textcolor(color) __set_gm(RESETATTR, color, 30) #define textbackground(color) __set_gm(RESETATTR, color, 40) #endif /* CONIO_H_ */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/copyfile.c000066400000000000000000000162371265065303600157140ustar00rootroot00000000000000/* Fastinit (finit) copyfile() implementation. * * Copyright (c) 2008 Claudio Matsuoka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include "lite.h" /* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */ static int adjust_target(char *src, char **dst) { int isdir = 0; if (fisdir(*dst)) { int slash = 0; char *tmp, *ptr = strrchr(src, '/'); if (!ptr) ptr = src; else ptr++; tmp = malloc(strlen(*dst) + strlen(ptr) + 2); if (!tmp) { errno = EISDIR; return 0; } isdir = 1; /* Free dst before exit! */ slash = fisslashdir(*dst); sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr); *dst = tmp; } return isdir; } /* Actual copy loop, used by both copyfile() and fcopyfile() * breaks loop on error and EOF */ static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len) { ssize_t ret = 0, size = 0; do { size_t count = num > len ? len : num; ret = read(in, buffer, count); if (ret <= 0) { if (ret == -1 && EINTR == errno) continue; break; } if (ret > 0) size += write(out, buffer, ret); num -= count; } while (num > 0); return size; } /** * copyfile - Copy a file to another. * @src: Full path name to source file. * @dst: Full path name to target file. * @len: Number of bytes to copy, zero (0) for entire file. * @sym: Should symlinks be recreated (1) or followed (0) * * This is a C implementation of the command line cp(1) utility. It is one * of the classic missing links in the UNIX C library. This version is from * the finit project, http://helllabs.org/finit/, which is a reimplementation * of fastinit for the Asus EeePC. * * Returns: * The number of bytes copied, zero may be error (check errno!), but it * may also indicate that @src was empty. If @src is a directory @errno * will be set to %EISDIR since copyfile() is not recursive. */ ssize_t copyfile(char *src, char *dst, int len, int sym) { char *buffer; int in, out, isdir = 0, saved_errno = 0; size_t num; ssize_t size = 0; struct stat st; errno = 0; buffer = malloc(BUFSIZ); if (!buffer) return 0; if (fisdir(src)) { saved_errno = EISDIR; /* Error: source is a directory */ goto exit; } /* Check if target is a directory, then append src filename. */ isdir = adjust_target(src, &dst); /* Check if the source file is a symlink ... */ if (stat(src, &st)) { size = -1; goto exit; } /* ... only try readlink() if symlink and @sym is set. */ if (sym && (st.st_mode & S_IFMT) == S_IFLNK) { size = readlink(src, buffer, BUFSIZ); if (size > 0) { if (size >= (ssize_t)BUFSIZ) { saved_errno = ENOBUFS; size = -1; } else { buffer[size] = 0; size = !symlink(buffer, dst); } } /* Don't fall back to copy, that is a potentially * dangerous race condition, see CWE-367. */ goto exit; } /* 0: copy entire file */ if (len == 0) num = st.st_size; else num = (size_t)len; in = open(src, O_RDONLY); if (in < 0) { saved_errno = errno; goto exit; } out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, fmode(src)); if (out < 0) { close(in); saved_errno = errno; goto exit; } size = do_copy(in, out, num, buffer, BUFSIZ); if (!size && errno) saved_errno = errno; close(out); close(in); exit: free(buffer); if (isdir) free(dst); errno = saved_errno; return size; } /** * movefile - Move a file to another location * @src: Source file. * @dst: Target file, or location. * * This is a C implementation of the command line mv(1) utility. * Usually the rename() API is sufficient, but not when moving across * file system boundaries. * * The @src argument must include the full path to the source file, * whereas the @dst argument may only be a directory, in which case the * same file name from @src is used. * * Returns: * POSIX OK(0), or non-zero with errno set. */ int movefile(char *src, char *dst) { int isdir, result = 0; /* Check if target is a directory, then append the src base filename. */ isdir = adjust_target(src, &dst); if (rename(src, dst)) { if (errno == EXDEV) { errno = 0; copyfile(src, dst, 0, 1); if (errno) result = 1; else result = remove(src); } else { result = 1; } } if (isdir) free(dst); return result; } /** * fcopyfile - Copy between FILE *fp. * @src: Source FILE. * @dst: Destination FILE. * * Function takes signals into account and will restart the syscalls as * long as error is %EINTR. * * Returns: * POSIX OK(0), or non-zero with errno set on error. */ int fcopyfile(FILE *src, FILE *dst) { int ret; char *buffer; if (!src || !dst) { errno = EINVAL; return -1; } buffer = malloc(BUFSIZ); if (!buffer) return -1; ret = do_copy(fileno(src), fileno(dst), BUFSIZ, buffer, BUFSIZ); if (ret > 0) ret = 0; else if (errno) ret = -1; free(buffer); return ret; } #ifdef UNITTEST #include int main(void) { int i = 0; char *files[] = { "/etc/passwd", "/tmp/tok", "/tmp/tok", "/etc/passwd", "/tmp/", "/tmp/passwd", "/etc/passwd", "/tmp", "/tmp/passwd", NULL }; FILE *src, *dst; printf("=>Start testing fcopyfile()\n"); while (files[i]) { printf("fcopyfile(%s, %s)\t", files[i], files[i + 1]); src = fopen(files[i], "r"); dst = fopen(files[i + 1], "w"); if (fcopyfile(src, dst)) { if (!fisdir(files[i + 1])) err(1, "Failed fcopyfile(%s, %s)", files[i], files[i + 1]); } if (src) fclose(src); if (dst) fclose(dst); if (fexist(files[i + 2])) printf("OK => %s", files[i + 2]); printf("\n"); erase(files[i + 2]); i += 3; } printf("\n=>Start testing copyfile()\n"); i = 0; while (files[i]) { printf("copyfile(%s, %s)\t", files[i], files[i + 1]); if (!copyfile(files[i], files[i + 1], 0, 0)) err(1, "Failed copyfile(%s, %s)", files[i], files[i + 1]); if (fexist(files[i + 2])) printf("OK => %s", files[i + 2]); printf("\n"); erase(files[i + 2]); i += 3; } return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f copyfile.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/copyfile.mk000066400000000000000000000001441265065303600160670ustar00rootroot00000000000000OBJS = fisdir.o fexist.o fmode.o TARGET = $(OBJS) copyfile.test clean include rules.mk pimd-2.3.2/libite/dir.c000066400000000000000000000127171265065303600146570ustar00rootroot00000000000000/* Functions for operating on files in directories. * * Copyright (c) 2008-2014 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include static const char *matcher_type = NULL; static int (*matcher_filter) (const char *file) = NULL; static int matcher(const struct dirent *entry) { char *pos = strrchr(entry->d_name, '.'); if (matcher_filter && !matcher_filter(entry->d_name)) /* User matcher overrides the rest. */ return 0; /* Skip current dir "." from list of files. */ if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') || (2 == strlen(entry->d_name) && !strcmp(entry->d_name, ".."))) return 0; /* filetype == "" */ if (matcher_type[0] == 0) return 1; /* Entry has no "." */ if (!pos) return 0; return !strcmp(pos, matcher_type); } /** * dir - List all files of a certain type in the given directory. * @dir: Base directory for dir operation. * @type: File type suffix, e.g. ".cfg". * @filter: Optional file name filter. * @list: Pointer to an array of file names. * @strip: Flag, if set dir() strips the file type. * * This function returns a @list of files, matching the @type suffix, * in the given directory @dir. * * The @list argument is a pointer to where to store the dynamically * allocated list of file names. This list should be free'd by first * calling free() on each file name and then on the list itself. * * If @filter is not %NULL it will be called for each file found. If * @filter returns non-zero the @file argument will be included in the * resulting @list. If @filter returns zero for given @file it will * be discarded. * * If the @strip flag is set the resulting @list of files has their * file type stripped, including the dot. So a match "config0.cfg" * would be returned as "config0". * * Returns: * Number of files in @list, zero if no matching files of @type. */ int dir(const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip) { int i, n, num = 0; char **files; struct dirent **namelist; assert(list); if (!dir) /* Assuming current directory */ dir = "."; if (!type) /* Assuming all files. */ type = ""; matcher_type = type; matcher_filter = filter; n = scandir(dir, &namelist, matcher, alphasort); if (n < 0) { perror("scandir"); } else if (n > 0) { files = (char **)malloc(n * sizeof(char *)); for (i = 0; i < n; i++) { if (files) { char *name = namelist[i]->d_name; char *type = strrchr(name, '.'); if (type && strip) *type = 0; files[i] = strdup(name); num++; } free(namelist[i]); } if (num) *list = files; } if (namelist) free(namelist); return num; } #ifdef UNITTEST #include #include #include #include "lite.h" #define DIR_TYPE_IMAGE ".img" #define DIR_TYPE_SYSLOG "" #define DIR_TYPE_CONFIG ".cfg" #define STARTUP_CONFIG "startup-config.cfg" int simulate_files(int creat) { int i; char *files[] = { "config0.cfg", "config1.cfg", "config2.cfg", "config3.cfg", "rr109.img", "rx100.img", "rx107.img", "rm957.img", "messages" }; for (i = 0; i < NELEMS(files); i++) { if (creat) touch(files[i]); else erase(files[i]); } if (creat) symlink("config2.cfg", STARTUP_CONFIG); else erase(STARTUP_CONFIG); return 0; } static int cfg_dir_filter(const char *file) { /* Skip the STARTUP_CONFIG file, it is a symbolic link to the * current startup configuration. */ return ! !strcmp(file, STARTUP_CONFIG); } int main(int argc, char *argv[]) { int i, num; char *type = DIR_TYPE_CONFIG; char **files; int once = 1; int is_startup_config(const char *entry) { static char file[80]; if (once) { int len = readlink(STARTUP_CONFIG, file, sizeof(file)); if (len == -1) return 0; file[len] = 0; once = 0; /* Only once per call to dir() */ } //printf ("Comparing link %s with entry %s\n", file, entry); return !strcmp(file, entry); } simulate_files(1); if (argc >= 2) { if (!strcasecmp("CONFIG", argv[1])) { type = DIR_TYPE_CONFIG; system("ls -l *" DIR_TYPE_CONFIG); } if (!strcasecmp("IMAGE", argv[1])) { type = DIR_TYPE_IMAGE; system("ls -l *" DIR_TYPE_IMAGE); } if (!strcasecmp("SYSLOG", argv[1])) { type = DIR_TYPE_SYSLOG; system("ls -l *"); } } num = dir(NULL, type, cfg_dir_filter, &files, 0); if (num) { for (i = 0; i < num; i++) { printf("%s", files[i]); if (is_startup_config(files[i])) printf(" --> startup-config"); printf("\n"); free(files[i]); } free(files); } simulate_files(0); return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f dir.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/dir.mk000066400000000000000000000000761265065303600150370ustar00rootroot00000000000000OBJS = TARGET = dir.test clean include rules.mk pimd-2.3.2/libite/fexist.c000066400000000000000000000043061265065303600153760ustar00rootroot00000000000000/* Check if file exists * * Copyright (c) 2008 Claudio Matsuoka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include /** * fexist - Check if a file exists in the file system. * @file: File to look for, with full path. * * Returns: * %TRUE(1) if the file exists, otherwise %FALSE(0). */ int fexist(char *file) { if (!file) { errno = EINVAL; return 0; /* Doesn't exist ... */ } if (-1 == access(file, F_OK)) return 0; return 1; } #ifdef UNITTEST #include "lite.h" int main(void) { int i = 0; struct { char *file; int exist; } arr[] = { { "/etc/passwd", 1 }, { "/etc/kalle", 0 }, { "/sbin/init", 1 }, { "/dev/null", 1 }, { NULL, 0 }, }; FILE *src, *dst; for (i = 0; i < NELEMS(arr); i++) { if (fexist(arr[i].file) != arr[i].exist) err(1, "Failed fexist(%s)", arr[i].file ?: "NULL"); else printf("File %-11s %-14s => OK!\n", arr[i].file ?: "NULL", arr[i].exist ? "does exist" : "does not exist"); } return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f fexist.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/fexist.mk000066400000000000000000000001011265065303600155500ustar00rootroot00000000000000OBJS = TARGET = fexist.test clean include rules.mk pimd-2.3.2/libite/fisdir.c000066400000000000000000000043651265065303600153610ustar00rootroot00000000000000/* Check if directory exists * * Copyright (c) 2008 Claudio Matsuoka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include /** * fexist - Check if a file exists and is a directory. * @file: File to look for, with full path. * * Returns: * %TRUE(1) if the file exists and is a directory, otherwise %FALSE(0). */ int fisdir(char *file) { struct stat sb; if (!stat(file, &sb) && S_ISDIR(sb.st_mode)) return 1; return 0; } #ifdef UNITTEST #include "lite.h" int main(void) { int i = 0; struct { char *file; int exist; } arr[] = { { "/etc/passwd", 0 }, { "/etc/", 1 }, { "/sbin/init", 0 }, { "/dev/null", 0 }, { "/dev/", 1 }, { NULL, 0 }, }; FILE *src, *dst; for (i = 0; i < NELEMS(arr); i++) { if (fisdir(arr[i].file) != arr[i].exist) err(1, "Failed fisdir(%s)", arr[i].file ?: "NULL"); else printf("fisdir() %-11s %-18s => OK!\n", arr[i].file ?: "NULL", arr[i].exist ? "is a directory" : "is NOT a directory"); } return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f fisdir.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/fisdir.mk000066400000000000000000000001011265065303600155260ustar00rootroot00000000000000OBJS = TARGET = fisdir.test clean include rules.mk pimd-2.3.2/libite/fmode.c000066400000000000000000000032531265065303600151660ustar00rootroot00000000000000/* File mode bit operations * * Copyright (c) 2008 Claudio Matsuoka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include /** * fmode - Return a file's mode bits. * @file: File to look for, with full path. * * Returns: * The mode bits or zero when @file does not exist and errno set to %ENOENT. */ mode_t fmode(char *file) { struct stat sb; if (!file) { errno = EINVAL; return 0; } if (!stat(file, &sb)) return sb.st_mode; return 0; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/fsendfile.c000066400000000000000000000061311265065303600160310ustar00rootroot00000000000000/* Copy data between file streams * * Copyright (c) 2013 Tobias Waldekranz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include /** * fsendfile - copy data between file streams * @src: Source stream * @dst: Destination stream * @len: Number of bytes to copy * * @dst may be %NULL, in which case @len bytes are read and discarded * from @src. This can be useful for streams where seeking is not * permitted. Additionally, @len may be the special value zero (0), in * which case fsendfile() will copy until %EOF is seen on @src. * * Returns: * The number of bytes copied. If an error is detected -1 is returned * and @errno will be set accordingly. */ size_t fsendfile(FILE *src, FILE *dst, size_t len) { char *buf; size_t blk = BUFSIZ, num = 0, tot = 0; if (!src) { errno = EINVAL; return -1; } buf = (char *)malloc(BUFSIZ); if (!buf) return -1; while (!len || tot < len) { if (len && ((len - tot) < BUFSIZ)) blk = len - tot; num = fread(buf, 1, blk, src); if (num == 0) break; if (dst && (fwrite(buf, num, 1, dst) != 1)) { num = -1; break; } tot += num; } free(buf); return (num == (size_t)-1) ? (size_t)-1 : tot; } #ifdef UNITTEST #include #include int main(void) { int i = 0; char *files[] = { "/etc/passwd", "/tmp/tok", "/etc/passwd", "/tmp/passwd", "/etc/passwd", "/tmp/passwd", NULL }; FILE *src, *dst; while (files[i]) { src = fopen(files[i], "r"); dst = fopen(files[i + 1], "w"); printf("fsendfile(%s, %s, 512)\t", files[i], files[i + 1]); if (-1 == fsendfile(src, dst, 512)) err(1, "Failed fsendfile(%s, %s)", files[i], files[i + 1]); if (!access(files[i + 1], F_OK)) printf("OK => %s\n", files[i + 1]); remove(files[i + 1]); i += 2; } return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f fsendfile.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/fsendfile.mk000066400000000000000000000001041265065303600162100ustar00rootroot00000000000000OBJS = TARGET = fsendfile.test clean include rules.mk pimd-2.3.2/libite/ifconfig.c000066400000000000000000000053741265065303600156660ustar00rootroot00000000000000/* Fastinit (finit) ifconfig() implementation. * * Copyright (c) 2008 Claudio Matsuoka * Copyright (C) 2009-2014 Joachim Nilsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include extern size_t strlcpy(char *dst, const char *src, size_t siz); /** * ifconfig - Basic ifconfig like operations on an interface * @ifname: Name of interface to operate on * @addr: If @up then set this optional IPv4 address * @mask: If @up and @addr, and @addr is not INADDR_ANY, then set netmask * @up: Control %IFF_UP flag on interface * * Returns: * POSIX OK(0) on success, or non-zero on error. */ int ifconfig(char *ifname, char *addr, char *mask, int up) { int sd, ret = -1; struct ifreq ifr; struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; if ((sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) return -1; memset(&ifr, 0, sizeof (ifr)); strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET; if (up) { if (addr) { if (inet_pton(AF_INET, addr, &sin->sin_addr) == 1) ret = ioctl(sd, SIOCSIFADDR, &ifr); } /* Non-zero IP address */ if (mask && addr && strcmp(addr, "0.0.0.0")) { if (inet_pton(AF_INET, mask, &sin->sin_addr) == -1) ret = ioctl(sd, SIOCSIFNETMASK, &ifr); } } if (!ioctl(sd, SIOCGIFFLAGS, &ifr)) { if (up) ifr.ifr_flags |= IFF_UP; else ifr.ifr_flags &= ~IFF_UP; ret = ioctl(sd, SIOCSIFFLAGS, &ifr); } close(sd); return ret; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/lfile.c000066400000000000000000000116621265065303600151720ustar00rootroot00000000000000/* Parse UNIX /etc configuration files like /etc/protocols and /etc/services * * Copyright (c) 2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* FILE */ #include /* atoi() */ #include /* strlen(), strncmp(), strtok_r() */ #include /* MAX() */ typedef struct lfile { FILE *fp; char buf[256]; char *sep, *save; } lfile_t; /** * lfopen - Open file and return parsing context * @file: File to parse * @sep: Separator(s) to use in lftok() * * Returns: * Pointer to a &lftile_t parser context, or %NULL on error. */ lfile_t *lfopen(char *file, char *sep) { lfile_t *lf; if (!file || !sep) { errno = EINVAL; return NULL; } /* Use calloc for clearing buf on behalf of lftok() */ lf = calloc(sizeof(*lf), sizeof(char)); if (lf) { lf->fp = fopen(file, "r"); lf->sep = sep; lf->save = lf->buf; if (!lf->fp) { free(lf); return NULL; } } return lf; } /** * lfclose - Close a parser context * @lf: Pointer to &lfile_t context from lfopen() */ void lfclose(lfile_t *lf) { if (!lf) return; if (lf->fp) fclose(lf->fp); free(lf); } /** * lftok - Get next token in file * @lf: Pointer to &lfile_t context from lfopen() * * Returns: * Next token, read from file previously opened with lfopen(), * or %NULL if EOF. */ char *lftok(lfile_t *lf) { char *token; if (!lf || !lf->fp || !lf->sep) { errno = EINVAL; return NULL; } token = strtok_r(NULL, lf->sep, &lf->save); if (!token) { while (fgets(lf->buf, sizeof(lf->buf), lf->fp)) { if (lf->buf[0] == '#') continue; token = strtok_r(lf->buf, lf->sep, &lf->save); if (!token) continue; return token; } errno = ENOENT; return NULL; } return token; } /** * lfgetkey - Find key in file * @lf: Pointer to &lfile_t context from lfopen() * @key: Key to look for * * Locate @key from the current position in the file parser context * returned from lfopen(). Please note, the search for @key does not * start from the beginning of the file, it searches from the current * position. To restart search from the beginning use rewind() on the * lf->fp. * * Returns: * The value to @key, or %NULL if not found. */ char *lfgetkey(lfile_t *lf, char *key) { char *token; while ((token = lftok(lf))) { if (token[0] == '#') continue; if (!strncmp(token, key, MAX(strlen(token), strlen(key)))) return lftok(lf); } return NULL; } /** * lfgetint - Same as lfgetkey() but returns an integer * @lf: Pointer to &lfile_t context from lfopen() * @key: Key to look for * * This function is the same as lfgetkey() but returns the positive * integer value for the matching @key, if found. * * Returns: * The positive integer value for @key, or -1 if not found. */ int lfgetint(lfile_t *lf, char *key) { char *token = lfgetkey(lf, key); if (token) return atoi(token); return -1; } /** * fgetint - Find the integer value for key in a file * @file: File to search for @key * @sep: Separator for tokens in @file * @key: Key to look for in @file * * This is a convenience wrapper for lfopen(), lfgetint(), and * lfclose(). * * Returns: * The positive integer value for @key, or -1 if not found. */ int fgetint(char *file, char *sep, char *key) { int val = -1; lfile_t *lf; lf = lfopen(file, sep); if (lf) { val = lfgetint(lf, key); lfclose(lf); } return val; } #ifdef UNITTEST int main(void) { int val; lfile_t lf; val = fgetint("/etc/protocols", " \n\t", "udp"); if (val == -1) { perror("Failed locating 'udp' protocol"); return 1; } printf("udp has proto %d\n", val); val = fgetint("/etc/services", " /\n\t", "ftp"); if (val == -1) { perror("Failed locating 'ftp' service"); return 1; } printf("ftp is inet port %d\n", val); val = fgetint("/etc/group", "x:\n", "utmp"); if (val == -1) { perror("Failed locating group 'utmp'"); return 1; } printf("utmp is gid %d\n", val); val = fgetint("/etc/passwd", "x:\n", "nobody"); if (val == -1) { perror("Failed locating user 'nobody'"); return 1; } printf("nobody is uid %d\n", val); return 0; } #endif /** * Local Variables: * compile-command: "make V=1 -f lfile.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/lfile.mk000066400000000000000000000001001265065303600153400ustar00rootroot00000000000000OBJS = TARGET = lfile.test clean include rules.mk pimd-2.3.2/libite/lite.h000066400000000000000000000145521265065303600150420ustar00rootroot00000000000000/* Collection of frog DNA * * Copyright (c) 2008-2010 Claudio Matsuoka * Copyright (c) 2008-2016 Joachim Nilsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef LITE_H_ #define LITE_H_ #include #include #include /* uint8_t, uint16_t, uint32_t, INT32_MAX, etc. */ #include #include #include #include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ #include typedef struct lfile lfile_t; char *chomp (char *str); int fexist (char *file); int fisdir (char *file); mode_t fmode (char *file); FILE *tempfile (void); ssize_t copyfile (char *src, char *dst, int len, int sym); int movefile (char *src, char *dst); int fcopyfile (FILE *src, FILE *dst); size_t fsendfile (FILE *src, FILE *dst, size_t len); int ifconfig (char *ifname, char *addr, char *mask, int up); lfile_t*lfopen (char *file, char *sep); void lfclose (lfile_t *lf); char *lftok (lfile_t *lf); char *lfgetkey (lfile_t *lf, char *key); int lfgetint (lfile_t *lf, char *key); int fgetint (char *file, char *sep, char *key); int mkpath (char *dir, mode_t mode); int makepath (char *dir); int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip); int rsync (char *src, char *dst, int delete, int (*filter) (const char *file)); int pidfile (const char *basename); int pidfile_signal(const char *pidfile, int signal); pid_t pidfile_read (const char *pidfile); pid_t pidfile_poll (const char *pidfile); #ifndef strlcpy size_t strlcpy (char *dst, const char *src, size_t siz); #endif #ifndef strlcat size_t strlcat (char *dst, const char *src, size_t siz); #endif #ifndef strtonum long long strtonum (const char *numstr, long long minval, long long maxval, const char **errstrp); #endif int tree(char *path, int show_perms); #ifndef touch # define touch(x) do { if (mknod((x), S_IFREG|0644, 0) && errno != EEXIST) warn("Failed creating %s", x); } while (0) #endif #ifndef makedir # define makedir(x, p) do { if (mkdir(x, p) && errno != EEXIST) warn("Failed creating directory %s", x); } while (0) #endif #ifndef makefifo # define makefifo(x, p) do { if (mkfifo(x, p) && errno != EEXIST) warn("Failed creating FIFO %s", x); } while (0) #endif #ifndef erase # define erase(x) do { if (remove(x) && errno != ENOENT) warn("Failed removing %s", x); } while (0) #endif #ifndef chardev # define chardev(x,m,maj,min) mknod((x), S_IFCHR|(m), makedev((maj),(min))) #endif #ifndef blkdev # define blkdev(x,m,maj,min) mknod((x), S_IFBLK|(m), makedev((maj),(min))) #endif #ifndef S_ISEXEC # define S_ISEXEC(m) (((m) & S_IXUSR) == S_IXUSR) #endif /* Unline isset(), setbit() et al, these work with integers/shorts/longwords/etc. */ #ifndef ISCLR #define ISCLR(word,bit) ((word & (1 << (bit)) ? 0 : 1)) #endif #ifndef ISSET #define ISSET(word,bit) ((word & (1 << (bit)) ? 1 : 0)) #endif #ifndef ISOTHER #define ISOTHER(word,bit) ((word & ~(1 << (bit)) ? 1 : 0)) /* Is any other bit set? */ #endif #ifndef SETBIT #define SETBIT(word,bit) (word |= (1 << (bit))) #endif #ifndef CLRBIT #define CLRBIT(word,bit) (word &= ~(1 << (bit))) #endif /* From The Practice of Programming, by Kernighan and Pike */ #ifndef NELEMS #define NELEMS(array) (sizeof(array) / sizeof(array[0])) #endif /* Mark a function variable as unused, useful for generic callbacks */ #ifndef UNUSED #define UNUSED(x) UNUSED_ ## x __attribute__ ((unused)) #endif /* Does directory end with a slash? */ static inline int fisslashdir(char *dir) { if (!dir) return 0; if (strlen(dir) > 0) return dir[strlen(dir) - 1] == '/'; return 0; } /* Convert string to natural number (0-2147483647), returns -1 on error. */ static inline int atonum(const char *str) { int val = -1; const char *errstr; if (str) { val = strtonum(str, 0, INT32_MAX, &errstr); if (errstr) return -1; } return val; } /* Validate string, non NULL and not zero length */ static inline int string_valid(const char *s) { return s && strlen(s); } /* Relaxed comparison, e.g., sys_string_match("small", "smaller") => TRUE */ static inline int string_match(const char *a, const char *b) { size_t min = MIN(strlen(a), strlen(b)); return !strncasecmp(a, b, min); } /* Strict comparison, e.g., sys_string_match("small", "smaller") => FALSE */ static inline int string_compare (const char *a, const char *b) { return strlen(a) == strlen(b) && !strcmp(a, b); } /* Strict comparison, like sys_string_compare(), but case insensitive, * e.g., sys_string_match("small", "SmAlL") => TRUE */ static inline int string_case_compare (const char *a, const char *b) { return strlen (a) == strlen (b) && !strcasecmp (a, b); } #define min(a,b) \ ({ \ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; \ }) #define max(a,b) \ ({ \ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; \ }) /* Compat */ #define copy_filep(src,dst) fcopyfile(src,dst) #define pidfile_read_pid(file) pifile_read(file) #define signal_pidfile(file,signo) pidfile_signal(file,signo) #endif /* LITE_H_ */ /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/m4/000077500000000000000000000000001265065303600142455ustar00rootroot00000000000000pimd-2.3.2/libite/m4/.gitignore000066400000000000000000000000051265065303600162300ustar00rootroot00000000000000*.m4 pimd-2.3.2/libite/makepath.c000066400000000000000000000052301265065303600156630ustar00rootroot00000000000000/* mkpath() -- Create all components leading up to a given directory * * Copyright (c) 2013 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include int mkpath(char *dir, mode_t mode) { if (!dir) { errno = EINVAL; return 1; } if (strlen(dir) == 1 && dir[0] == '/') return 0; mkpath(dirname(strdupa(dir)), mode); return mkdir(dir, mode); } /** * makepath - Create all components of the specified directory. * @dir: Directory to create. * * Returns: * POSIX OK (0) on success, otherwise -1 and errno set appropriately. * This function returns EINVAL on bad argument, or ENOMEM when it * fails allocating temporary memory. For other error codes see the * mkdir() syscall description. */ int makepath(char *dir) { return mkpath(dir, 0777); } /********************************* UNIT TESTS ************************************/ #ifdef UNITTEST #include "lite.h" int checkpath(char *dir) { char tmp[256]; struct stat sb; snprintf(tmp, sizeof(tmp), "ls -ld %s", dir); if (system(tmp)) perror("system"); if (!stat(dir, &sb) && S_ISDIR(sb.st_mode)) return 0; errno = ENOTDIR; return 1; } int test_makepath(char *dir) { int ret = makepath(dir); if (!ret) ret = checkpath(dir); if (ret) perror("Failed"); return ret; } int main(void) { int i, ret = 0; char *list[] = { "/tmp/tok/", "/tmp/tok2", "/tmp/ab", "/tmp/b", "/tmp/a/", "/tmp/a/b", "/tmp/a/c/", NULL }; for (i = 0; list[i]; i++) rmdir(list[i]); printf("Testing makepath() ...\n"); for (i = 0; list[i] && !ret; i++) ret = test_makepath(list[i]); printf("\nCleaning up ...\n"); for (i = 0; list[i]; i++) rmdir(list[i]); return ret; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f makepath.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/makepath.mk000066400000000000000000000001031265065303600160420ustar00rootroot00000000000000OBJS = TARGET = makepath.test clean include rules.mk pimd-2.3.2/libite/pidfile.c000066400000000000000000000062651265065303600155160ustar00rootroot00000000000000/* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */ /* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #ifndef pidfile static char *pidfile_path = NULL; static pid_t pidfile_pid; static void pidfile_cleanup(void); const char *__pidfile_name = NULL; extern char *__progname; int pidfile(const char *basename) { int save_errno; pid_t pid; FILE *f; if (basename == NULL) basename = __progname; if (pidfile_path != NULL) { free(pidfile_path); pidfile_path = NULL; __pidfile_name = NULL; } /* _PATH_VARRUN includes trailing / */ if (asprintf(&pidfile_path, "%s%s.pid", _PATH_VARRUN, basename) == -1) return (-1); if ((f = fopen(pidfile_path, "w")) == NULL) { save_errno = errno; free(pidfile_path); pidfile_path = NULL; errno = save_errno; return (-1); } pid = getpid(); if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) { save_errno = errno; (void) fclose(f); (void) unlink(pidfile_path); free(pidfile_path); pidfile_path = NULL; errno = save_errno; return (-1); } (void) fclose(f); /* * LITE extension, no need to set up another atexit() handler * if user only called us to update the mtime of the PID file */ if (pidfile_pid == pid) return (0); pidfile_pid = pid; if (atexit(pidfile_cleanup) < 0) { save_errno = errno; (void) unlink(pidfile_path); free(pidfile_path); pidfile_path = NULL; pidfile_pid = 0; errno = save_errno; return (-1); } __pidfile_name = pidfile_path; return (0); } static void pidfile_cleanup(void) { if (pidfile_path != NULL && pidfile_pid == getpid()) (void) unlink(pidfile_path); } #endif pimd-2.3.2/libite/pidfilefn.c000066400000000000000000000115301265065303600160310ustar00rootroot00000000000000/* Functions for dealing with PID files (client side) * * Copyright (c) 2009-2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include extern char *__pidfile_name; extern char *chomp(char *str); /** * pidfile_read - Reads a PID value from a pidfile. * @pidfile: File containing PID, usually in /var/run/.pid * * This function takes a @pidfile and returns the PID found therein. * * Returns: * On invalid @pidfile -1 and errno set to %EINVAL, when @pidfile does not exist -1 * and errno set to %ENOENT. When the pidfile is empty or when its contents cannot * be translated this function returns zero (0), on success this function returns * a PID value greater than one. PID 1 is reserved for the system init process. */ pid_t pidfile_read(const char *pidfile) { int pid = 0; char buf[16]; FILE *fp; if (!pidfile) { errno = EINVAL; return -1; } fp = fopen(pidfile, "r"); if (!fp) return -1; if (fgets(buf, sizeof(buf), fp)) { char *ptr = chomp(buf); if (ptr) { errno = 0; pid = strtoul(ptr, NULL, 0); if (errno) pid = 0; /* Failed conversion. */ } } fclose(fp); return pid; } /** * pidfile_poll - Poll for the existence of a pidfile and return PID * @pidfile: Path to pidfile to poll for * * This function polls for the pidfile at @pidfile for at most 5 seconds * before timing out. If the file is created within that time span the * file is read and its PID contents returned. * * Returns: * The PID read from @pidfile, or zero on timeout. */ pid_t pidfile_poll(const char *pidfile) { pid_t pid = 0; int tries = 0; /* Timeout = 100 * 50ms = 5s */ while ((pid = pidfile_read(pidfile)) <= 0 && tries++ < 100) usleep(50000); /* Wait 50ms between retries */ if (pid < 0) pid = 0; return pid; } /** * pidfile_signal - Send signal to a PID and cleanup pidfile afterwards * @pidfile: File containing PID, usually in /var/run/.pid * @signal: Signal to send to PID found in @pidfile. * * If @signal is any of %SIGTERM, %SIGKILL, or if kill() returns -1 the * @pidfile is removed. * * Returns: * POSIX OK(0) on success, non-zero otherwise. */ int pidfile_signal(const char *pidfile, int signal) { int pid = -1, ret = -1; pid = pidfile_read(pidfile); if (pid <= 0) return 1; ret = kill(pid, signal); if ((ret == -1) || (signal == SIGTERM) || (signal == SIGKILL)) (void)remove(pidfile); return 0; } /********************************* UNIT TESTS ************************************/ #ifdef UNITTEST #include #include "lite.h" extern char *__progname; static char PIDFILE[42]; static void sigterm_handler(int UNUSED(signo)) { printf("Exiting ...\n"); } static void sigalrm_handler(int UNUSED(signo)) { printf("Testing pidfile() ...\n"); pidfile(NULL); if (!fexist(PIDFILE)) err(1, "pidfile() failed creating %s", PIDFILE); } int main(void) { char cmd[80]; snprintf(PIDFILE, sizeof(PIDFILE), "%s%s.pid", _PATH_VARRUN, __progname); signal(SIGTERM, sigterm_handler); signal(SIGALRM, sigalrm_handler); alarm(1); printf("Testing pidfile_poll() ...\n"); if (!pidfile_poll(PIDFILE)) printf("Timed out!\n"); else printf("We got signal!\n"); printf("Reading pid file, should be %d ...\n", getpid()); printf("=> %d\n", pidfile_read(PIDFILE)); printf("\nComparing __pidfile_name with our guessed PID filename ...\n"); printf("strcmp(\"%s\",\n \"%s\") => %s\n\n", __pidfile_name, PIDFILE, !strcmp(__pidfile_name, PIDFILE) ? "OK" : "FAIL"); /* Occular verification that calling pidfile() again updates mtime */ snprintf(cmd, sizeof(cmd), "ls -l --full-time %s", PIDFILE); system(cmd); printf("Before ^^ pidfile() vv after ...\n"); pidfile(NULL); system(cmd); printf("Signalling ourselves ...\n"); return pidfile_signal(PIDFILE, SIGTERM); } #endif /* UTEST_PIDFN */ /** * Local Variables: * compile-command: "make V=1 -f pidfilefn.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/pidfilefn.mk000066400000000000000000000002351265065303600162160ustar00rootroot00000000000000CFLAGS = -W -Wall -Wextra -Werror OBJS = pidfile.o chomp.o fexist.o ASROOT = 1 TARGET = clean $(OBJS) pidfilefn.test include rules.mk pimd-2.3.2/libite/progress.c000066400000000000000000000074011265065303600157370ustar00rootroot00000000000000/* Simple termios based progress bar * * Copyright (c) 2012-2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* INT_MAX */ #include #include #include "conio.h" #define SPINNER_THROB ".oOo" #define SPINNER_PULSAR ".oO°Oo." #define SPINNER_ARROW "v<^>" #define SPINNER_STAR ".oO@*" #define SPINNER_DEFAULT "|/-\\" static char spinner(char *style) { size_t num; static unsigned int i = 0; if (!style) style = SPINNER_DEFAULT; num = strlen(style); return style[i++ % num]; /* % Number of states in style */ } /** * progress - Advanced ASCII progress bar with spinner * @percent: Start first call with this set to 0, end with 100 * @max_width: Max width of progress bar, in total characters. * * This function draws an advanced ASCII progressbar at the current * line. It always start from the first column. * * The progress bar will hide the cursor if started with @percent 0 and * show it again at the end, when called with @percent 100. * * While being called with the same percentage the spinner will spin, * to show the user the process hasn't frozen. * * If the output TTY cannot interpret control characters, like \r, it is * advised to instead used the progress_simple() function. */ void progress(int percent, int max_width) { int i, bar; /* Adjust for progress bar overhead */ max_width -= 10; if (0 == percent) hidecursor(); fprintf(stderr, "\r%3d%% %c [", percent, spinner(NULL)); bar = percent * max_width / 100; for (i = 0; i < max_width; i++) { if (i > bar) fputc(' ', stderr); else if (i == bar) fputc('>', stderr); else fputc('=', stderr); } fprintf(stderr, "]"); if (100 == percent) { showcursor(); fputc('\n', stderr); } } void progress_simple(int percent) { static int last = 1; int ratio, numout; if (!percent && last) { last = 0; fputs("0% 25% 50% 75% 100%\n" "|---------+---------+---------+---------|\n" "|", stderr); return; } ratio = 40 * percent / 100; numout = ratio - last; if (ratio <= last) return; last = ratio; while (numout--) { if (ratio != 40 || numout) putc('=', stderr); else putc('|', stderr); } } #ifdef UNITTEST #include /* atexit() */ #include /* usleep() */ #define MAX_WIDTH 80 #define msleep(msec) usleep(msec * 1000) static void bye(void) { showcursor(); } static void testit(int fn, int percent) { if (fn) progress(percent, MAX_WIDTH); else progress_simple(percent); } static void test(int fn) { int i, percent, block = 0, num = 85; while (block < num) { percent = block * 100 / num; for (i = 0; i < 10; i++) { testit(fn, percent); msleep(5); } block++; msleep(10); } testit(fn, 100); } int main(void) { atexit(bye); hidecursor(); printf("\nAdvanced:\n"); test(1); printf("\nSimple:\n"); test(0); printf("\n"); return 0; } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f progress.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/progress.mk000066400000000000000000000001031265065303600161140ustar00rootroot00000000000000OBJS = TARGET = progress.test clean include rules.mk pimd-2.3.2/libite/queue.h000066400000000000000000000436211265065303600152300ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.43 2015/12/28 19:38:40 millert Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues and XOR simple queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * An XOR simple queue is used in the same way as a regular simple queue. * The difference is that the head structure also includes a "cookie" that * is XOR'd with the queue pointer (first, last or next) to generate the * real pointer value. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ _Q_INVALIDATE((elm)->field.sle_next); \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods. */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_CONCAT(head1, head2) do { \ if (!SIMPLEQ_EMPTY((head2))) { \ *(head1)->sqh_last = (head2)->sqh_first; \ (head1)->sqh_last = (head2)->sqh_last; \ SIMPLEQ_INIT((head2)); \ } \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue access methods. */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (0) #endif /* !_SYS_QUEUE_H_ */ pimd-2.3.2/libite/rsync.c000066400000000000000000000155061265065303600152360ustar00rootroot00000000000000/* Micro "rsync" implementation. * * Copyright (c) 2011, 2012 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* NULL, free() */ #include /* strlen() */ #include /* rindex() */ #include #include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ #include #include #include "lite.h" static int copy(char *src, char *dst); static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode); static int prune(char *dst, char **new_files, int new_num); /** * rsync - Synchronize contents and optionally remove non-existing backups * @src: Source directory * @dst: Destination directory * @delete: Prune files from @dst that no longer exist in @src. * @filter: Optional filtering function for source directory. * * This is a miniature implementation of the famous rsync for local use only. * In fact, it is not even a true rsync since it copies all files from @src * to @dst. The @delete option is useful for creating backups, when set all * files removed from src since last backup are pruned from the destination * (backup) directory. * * The filter callback, @filter, if provided, is used to determine what files to * include from the source directory when backing up. If a file is to be skipped * the callback should simply return zero. * * Returns: * POSIX OK(0), or non-zero with @errno set on error. */ int rsync(char *src, char *dst, int delete, int (*filter) (const char *file)) { char source[256]; char dest[256]; int i = 0, num = 0, result = 0; char **files; /* Array of file names. */ if (!fisdir(dst)) makedir(dst, 0755); if (!fisdir(src)) { if (!fexist(src)) return 1; if (copy(src, dst)) result++; return errno; } /* Copy dir as well? */ if (!fisslashdir(src)) { char *ptr = rindex(src, '/'); if (!ptr) ptr = src; else ptr++; if (mdir(dest, sizeof(dest), dst, ptr, fmode(src))) return 1; dst = dest; } num = dir(src, "", filter, &files, 0); for (i = 0; i < num; i++) { /* Recursively copy sub-directries */ snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]); if (fisdir(source)) { char dst2[256]; strcat(source, "/"); if (mdir (dst2, sizeof(dst2), dst, files[i], fmode(source))) { result++; continue; } rsync(source, dst2, delete, filter); continue; /* Next file/dir in @src to copy... */ } if (copy(source, dst)) result++; } /* We ignore any errors from the pruning, that phase albeit useful is only * cosmetic. --Jocke 2011-03-24 */ if (delete) prune(dst, files, num); if (num) { for (i = 0; i < num; i++) free(files[i]); free(files); } return result; } static int copy(char *src, char *dst) { errno = 0; copyfile(src, dst, 0, 1); if (errno) { if (errno != EEXIST) return 1; errno = 0; } return 0; } /* Creates dir/name @mode ... skipping / if dir already ends so. */ static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode) { snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name); if (mkdir(buf, mode)) { if (EEXIST != errno) return 1; errno = 0; } return 0; } static int find(char *file, char **files, int num) { int n; for (n = 0; n < num; n++) if (!strncmp (files[n], file, MAX(strlen(files[n]), strlen(file)))) return 1; return 0; } /* Prune old files, no longer existing on source, from destination directory. */ static int prune(char *dst, char **new_files, int new_num) { int num, result = 0; char **files; num = dir(dst, "", NULL, &files, 0); if (num) { int i; for (i = 0; i < num; i++) { if (!find(files[i], new_files, new_num)) { char *name; size_t len = strlen(files[i]) + 2 + strlen(dst); name = malloc(len); if (name) { snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]); if (remove(name)) result++; free(name); } } free(files[i]); } free(files); } return result; } #ifdef UNITTEST #define BASE "/tmp/.unittest/" #define SRC BASE "src/" #define DST BASE "dst/" static int verbose = 0; static char *files[] = { SRC "sub1/1.tst", SRC "sub1/2.tst", SRC "sub1/3.tst", SRC "sub2/4.tst", SRC "sub2/5.tst", SRC "sub2/6.tst", SRC "sub3/7.tst", SRC "sub3/8.tst", SRC "sub3/9.tst", NULL }; void cleanup_test(void) { system("rm -rf " BASE); } void setup_test(void) { int i; char cmd[256]; mode_t dir_modes[] = { 755, 700 }; mode_t file_modes[] = { 644, 600 }; cleanup_test(); mkdir(BASE, 0755); mkdir(SRC, 0755); mkdir(DST, 0755); for (i = 0; files[i]; i++) { snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`", dir_modes[i % 2], files[i]); system(cmd); snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i], file_modes[i % 2], files[i]); system(cmd); } } static void check_tree(char *heading, char *dir) { if (verbose) { char cmd[128]; if (heading) puts(heading); tree(dir, 1); } } int run_test(void) { int result = 0; #if 0 setup_test(); check_tree("Before:", BASE); result += rsync(SRC, DST, 0, NULL); check_tree("After:", BASE); cleanup_test(); #endif setup_test(); result += rsync(BASE "src", DST, 0, NULL); check_tree("Only partial rsync of src <-- No slash!", BASE); #if 0 cleanup_test(); setup_test(); result += rsync(BASE "src/sub1", BASE "dst", 0, NULL); check_tree("Only partial rsync of src/sub1 <-- No slashes!!", BASE); cleanup_test(); setup_test(); result += rsync(BASE "src/sub1/", DST, 0, NULL); check_tree("Only partial rsync of src/sub1/", BASE); cleanup_test(); setup_test(); result += rsync(BASE "src/sub1", DST, 0, NULL); check_tree("Only partial rsync of src/sub1 <-- No slash!", BASE); result += rsync("/etc", "/var/tmp", 0, NULL); check_tree("Real life test:", "/var/tmp"); #endif return result; } int main(int argc, char *argv[]) { if (argc > 1) verbose = !strncmp("-v", argv[1], 2); atexit(cleanup_test); return run_test(); } #endif /* UNITTEST */ /** * Local Variables: * compile-command: "make V=1 -f rsync.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/rsync.mk000066400000000000000000000001711265065303600154130ustar00rootroot00000000000000OBJS = dir.o copyfile.o fexist.o fisdir.o fmode.o tree.o TARGET = clean $(OBJS) rsync.test include rules.mk pimd-2.3.2/libite/rules.mk000066400000000000000000000032051265065303600154100ustar00rootroot00000000000000.PHONY: all clean distclean # Figure out root of library, unless used as submodule ROOTDIR ?= $(shell pwd) CC ?= $(CROSS)gcc AR ?= $(CROSS)ar STRIP ?= $(CROSS)strip INSTALL := install --backup=off STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755 CPPFLAGS += -D_GNU_SOURCE ARFLAGS = crus # Default to silent build, use V=1 to get verbose mode. ifeq ($V,1) Q = PRINT = @true REDIRECT = MAKEFLAGS = VERBOSE = -v else Q = @ PRINT = @printf REDIRECT = >/dev/null MAKEFLAGS = --no-print-directory --silent VERBOSE = endif # Some tests may need to be run as root, e.g. pidfile() ifeq ($(ASROOT), 1) SUDO = sudo -n else SUDO = endif # Default install paths prefix ?= /usr/local libdir ?= $(prefix)/lib datadir ?= $(prefix)/share/doc/$(LIBNAME) incdir ?= $(prefix)/include # Pretty printing and GCC -M for auto dep files %.o: %.c $(PRINT) " CC $(subst $(ROOTDIR)/,,$(shell pwd)/)$@\n" $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) -c -MMD -MP -o $@ $< %: %.o $(PRINT) " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/)$@\n" $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@))) .PHONY: %.test %.test: %.c $(PRINT) " TEST $(subst $(ROOTDIR)/,,$(shell pwd)/)$(@:.test=)\n" $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) -g -o $@ -DUNITTEST $< $(OBJS) $(Q)$(RM) $(OBJS) $(Q)$(SUDO) ./$@ $(VERBOSE) $(REDIRECT) # Default build rules for both main and unit test makefiles all:: $(TARGET) clean:: -$(Q)$(RM) $(OBJS) $(DEPS) $(TARGET) $(SOLIB) $(STATICLIB) distclean:: -$(Q)$(RM) $(JUNK) unittest *.o *.a *.so* *.unittest *.test pimd-2.3.2/libite/strlcat.c000066400000000000000000000033511265065303600155470ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifndef strlcat /* * Appends src to string dst of size dsize (unlike strncat, dsize is the * full size of dst, not space left). At most dsize-1 characters * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). * Returns strlen(src) + MIN(dsize, strlen(initial dst)). * If retval >= dsize, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t dsize) { const char *odst = dst; const char *osrc = src; size_t n = dsize; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end. */ while (n-- != 0 && *dst != '\0') dst++; dlen = dst - odst; n = dsize - dlen; if (n-- == 0) return(dlen + strlen(src)); while (*src != '\0') { if (n != 0) { *dst++ = *src; n--; } src++; } *dst = '\0'; return(dlen + (src - osrc)); /* count does not include NUL */ } #endif pimd-2.3.2/libite/strlcpy.c000066400000000000000000000031261265065303600155730ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #ifndef strlcpy /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } #endif pimd-2.3.2/libite/strtonum.c000066400000000000000000000034001265065303600157610ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifndef strtonum #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #endif pimd-2.3.2/libite/tempfile.c000066400000000000000000000045151265065303600157030ustar00rootroot00000000000000/* A secure tmpfile() replacement. * * Copyright (c) 2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* O_TMPFILE requires -D_GNU_SOURCE */ #include #include /* mkstemp() */ #include /* fdopen() */ #include /* umask() */ #ifndef O_TMPFILE /* Too old GLIBC or kernel */ #warning O_TMPFILE missing on your system, tempfile() may not work! #define O_TMPFILE 020200000 /* Define and let it fail at runtime */ #endif /** * tempfile - A secure tmpfile() replacement * * This is the secure replacement for tmpfile() that does not exist in * GLIBC. The function uses the Linux specific %O_TMPFILE and %O_EXCL * for security. When the %FILE is fclose()'ed the file contents is * lost. The file is hidden in the %_PATH_TMP directory on the system. * * This function requires Linux 3.11, or later, due to %O_TMPFILE. * * Returns: * An open %FILE pointer, or %NULL on error. */ FILE *tempfile(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) int fd; mode_t oldmask; oldmask = umask(0077); fd = open(_PATH_TMP, O_TMPFILE | O_RDWR | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR); umask(oldmask); if (-1 == fd) return NULL; return fdopen(fd, "rw"); #else #warning Too old kernel, reverting to wrap unsafe tmpfile() ... return tmpfile(); #endif } #ifdef UNITTEST int main(void) { FILE *fp = tempfile(); system("ls -lrt " _PATH_TMP " | tail -10"); return fclose(fp); } #endif /** * Local Variables: * compile-command: "make V=1 -f tempfile.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/tempfile.mk000066400000000000000000000001031265065303600160550ustar00rootroot00000000000000OBJS = TARGET = tempfile.test clean include rules.mk pimd-2.3.2/libite/tree.c000066400000000000000000000071421265065303600150340ustar00rootroot00000000000000/* Simple /bin/tree replacement * * Copyright (c) 2015 Joachim Nilsson * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include static int all = 0; static int filter(const struct dirent *entry) { /* Skip current dir ".", and prev dir "..", from list of files */ if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') || (2 == strlen(entry->d_name) && !strcmp(entry->d_name, ".."))) return 0; if (!all && entry->d_name[0] == '.') return 0; return 1; } static void get_perms(struct stat *st, char *buf, size_t len) { mode_t m = st->st_mode; snprintf(buf, len, "[%c%c%c%c%c%c%c%c%c%c] ", S_ISCHR(m) ? 'c' : S_ISBLK(m) ? 'b' : S_ISFIFO(m) ? 'p' : S_ISLNK(m) ? 'l' : S_ISSOCK(m) ? 's' : '-', (m & S_IRUSR) ? 'r' : '-', (m & S_IWUSR) ? 'w' : '-', (m & S_ISUID ) ? 's' : (m & S_IXUSR) ? 'x' : '-', (m & S_IRGRP) ? 'r' : '-', (m & S_IWGRP) ? 'w' : '-', (m & S_ISGID) ? 's' : (m & S_IXGRP) ? 'x' : '-', (m & S_IROTH) ? 'r' : '-', (m & S_IWOTH) ? 'w' : '-', (m & S_IXOTH) ? 'x' : '-' ); } static int descend(char *path, int show_perms, char *pfx) { int result = 0; struct stat st; if (-1 == lstat(path, &st)) return 1; if ((st.st_mode & S_IFMT) == S_IFDIR) { int i, n; struct dirent **namelist = NULL; n = scandir(path, &namelist, filter, alphasort); if (n) { for (i = 0; i < n; i++) { char t = ' ', p[14] = "", s[256] = ""; char buf[256]; char dir[80]; if (i + 1 == n) { printf("%s `- ", pfx); snprintf(dir, sizeof(dir), "%s ", pfx); } else { printf("%s|-- ", pfx); snprintf(dir, sizeof(dir), "%s| ", pfx); } snprintf(buf, sizeof(buf), "%s/%s", path, namelist[i]->d_name); if (!lstat(buf, &st)) { if (show_perms) get_perms(&st, p, sizeof(p)); if ((st.st_mode & S_IFMT) == S_IFDIR) t = '/'; if (S_ISLNK(st.st_mode)) { snprintf(s, sizeof(s), "-> "); if (-1 == readlink(buf, &s[3], sizeof(s) - 3)) s[0] = 0; } } printf("%s%s%c%s\n", p, namelist[i]->d_name, t, s); if (t == '/') result += descend(buf, show_perms, dir); free(namelist[i]); } free(namelist); } } else { errno = ENOTDIR; result = -1; } return result; } int tree(char *path, int show_perms) { printf("[%s]\n", path); return descend(path, show_perms, ""); } #ifdef UNITTEST #include "lite.h" int main(void) { int i, result = 0; struct { char *path; int fail; } arr[] = { { "/etc/passwd", -1 }, { "/dev", 0 }, { "/sbin", 0 }, }; for (i = 0; i < NELEMS(arr); i++) { if (tree(arr[i].path, 1) != arr[i].fail) result++; } return result; } #endif /** * Local Variables: * compile-command: "make V=1 -f tree.mk" * version-control: t * indent-tabs-mode: t * c-file-style: "linux" * End: */ pimd-2.3.2/libite/tree.h000066400000000000000000000610631265065303600150430ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) #endif /* _SYS_TREE_H_ */ pimd-2.3.2/libite/tree.mk000066400000000000000000000001071265065303600152130ustar00rootroot00000000000000OBJS = TARGET = clean $(OBJS) tree.test include rules.mk