diff --git a/.gitignore b/.gitignore index 516ed611..48e6b38b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ build *~ *.loT m4/*.m4 +!m4/ax_sys_weak_alias.m4 diff --git a/COPYING b/COPYING index a43ea212..b8cf3a1a 100644 --- a/COPYING +++ b/COPYING @@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest diff --git a/COPYING.LIB b/COPYING.LIB index 161a3d1d..2b4628e4 100644 --- a/COPYING.LIB +++ b/COPYING.LIB @@ -51,7 +51,7 @@ library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. - + Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect @@ -98,7 +98,7 @@ works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. - + GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -145,7 +145,7 @@ Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - + 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 @@ -203,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. - + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. @@ -254,7 +254,7 @@ Library will still fall under Section 6.) distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - + 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work @@ -308,7 +308,7 @@ restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - + 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined @@ -349,7 +349,7 @@ subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -401,7 +401,7 @@ conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - + 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is @@ -435,7 +435,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest diff --git a/HACKING.tex b/HACKING.tex index a49113fb..54931d12 100644 --- a/HACKING.tex +++ b/HACKING.tex @@ -105,6 +105,68 @@ \section{GUIDELINES FOR HACKING ON QUAGGA} See also section~\ref{sec:dll-versioning} below regarding SHARED LIBRARY VERSIONING. +\section{YOUR FIRST CONTRIBUTIONS} + +Routing protocols can be very complex sometimes. Then, working with an +Opensource community can be complex too, but usually friendly with +anyone who is ready to be willing to do it properly. + +\begin{itemize} + + \item First, start doing simple tasks. Quagga's patchwork is a good place + to start with. Pickup some patches, apply them on your git trie, + review them and send your ack't or review comments. Then, a + maintainer will apply the patch if ack't or the author will + have to provide a new update. It help a lot to drain the + patchwork queues. + See \url{http://patchwork.quagga.net/project/quagga/list/} + + \item The more you'll review patches from patchwork, the more the + Quagga's maintainers will be willing to consider some patches you will + be sending. + + \item start using git clone, pwclient \url{http://patchwork.quagga.net/help/pwclient/} + +\begin{verbatim} +$ pwclient list -s new +ID State Name +-- ----- ---- +179 New [quagga-dev,6648] Re: quagga on FreeBSD 4.11 (gcc-2.95) +181 New [quagga-dev,6660] proxy-arp patch +[...] + +$ pwclient git-am 1046 +\end{verbatim} + +\end{itemize} + +\section{HANDY GUIDELINES FOR MAINTAINERS} + +Get your cloned trie: +\begin{verbatim} + git clone vjardin@git.sv.gnu.org:/srv/git/quagga.git +\end{verbatim} + +Apply some ack't patches: +\begin{verbatim} + pwclient git-am 1046 + Applying patch #1046 using 'git am' + Description: [quagga-dev,11595] zebra: route_unlock_node is missing in "show ip[v6] route " commands + Applying: zebra: route_unlock_node is missing in "show ip[v6] route " commands +\end{verbatim} + +Run a quick review. If the ack't was not done properly, you know who you have +to blame. + +Push the patches: +\begin{verbatim} + git push +\end{verbatim} + +Set the patch to accepted on patchwork +\begin{verbatim} + pwclient update -s Accepted 1046 +\end{verbatim} \section{COMPILE-TIME CONDITIONAL CODE} @@ -207,7 +269,7 @@ \section{COMMIT MESSAGES} detect loss of frob. * frob.h: (struct frob) Add DOWN state flag. -* frob.c: (frob\_change) set/clear DOWN appropriately on state change. +* frob.c: (frob_change) set/clear DOWN appropriately on state change. * bar.c: (barinate) Check frob for DOWN state. \end{verbatim}\end{quote} diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt index ec4e799d..11c85b1a 100644 --- a/INSTALL.quagga.txt +++ b/INSTALL.quagga.txt @@ -16,23 +16,23 @@ workarounds for POSIX non-compliance are welcome. It is considered a bug if Quagga fails to build and run on any of the following systems (where .x indicates the most recent release), or -such systems "-current" versions. (Note that considering it a bug is +such systems "-current" versions. Or, it might be that this list is +out of date and will be updated. (Note that considering it a bug is not a guarantee of support, merely "we agree that it is broken".) Dragonfly ? - FreeBSD 4.x [In 2007, this is getting tenous.] - FreeBSD 5.x - FreeBSD 6.x + FreeBSD (stable branches currently supported, plus perhaps one) FreeBSD-current Linux [kernel/distribution information needed] - NetBSD 2.x [Note texinfo 4.6 in base system] - NetBSD 3.x NetBSD 4.x + NetBSD 5.x + NetBSD 6.x NetBSD-current OpenBSD ? [info needed on what should work] - Solaris 9 - Solaris 10 + Solaris (modern/supported versions, including OpenSolaris forks) +On BSD systems, installing libexecinfo is strongly recommended in order +to get backtrace support. For further Quagga specific information on 'configure' and build-time configuration of the software, please read the Quagga info @@ -49,9 +49,9 @@ The Quagga website (http://www.quagga.net) currently has the info files available in various formats. -------------------------------------------------------------------------- -Building Quagga from CVS checkouts: +Building Quagga from git checkouts: -In order to build from CVS, you will need recent versions of several GNU +In order to build from git, you will need recent versions of several GNU tools, particularly autoconf, automake, libtool, GNU awk and texinfo. Note that the CVS snapshots on the Quagga website should not require these tools; everything is already setup ready to run 'configure'. If you have trouble @@ -64,13 +64,19 @@ a bug. Required versions can be moved earlier if no problems, or later after a judgement that a system without a higher version is deficient is made. + [TODO: this list is out of date as of 2013-07] automake: 1.9.6 (released 2005-07-10) autoconf: 2.59 (2.60 on 2006-06-26 is too recent to require) libtool: 1.5.22 (released 2005-12-18) texinfo: 4.7 (released 2004-04-10; 4.8 is not yet common) GNU AWK: 3.1.5 (released 2005-08-12) -Becuase some systems provide texinfo 4.6 (4.7 is new), quagga.info is +For running tests, one also needs: + + DejaGnu: + +[TODO: texinfo 4.6 is now ancient and this should be revisited/fixed] +Because some systems provide texinfo 4.6 (4.7 is new), quagga.info is checked in so that texinfo will generally not be invoked. When texinfo 4.7 is widespread, quagga.info will be removed from CVS and texinfo will become required again. (4.7 has figure support, needed @@ -91,11 +97,12 @@ instructions. Notes on required versions: The general goal is to use a modern baseline of tools, while not -imposing pain on those tracking stable distributions. The notes below -explain what versions are present in various environments. +imposing pain on those tracking supported (or almost supported) stable +distributions. The notes below explain what versions are present in +various environments. -NetBSD 1.6 and 2 provide texinfo 4.6. This is now considered old. -NetBSD 3 and 4 provide texinfo 4.7. +NetBSD 4 provides texinfo 4.7. +NetBSD 5 and 6 provides texinfo 4.8 Fedora Core ? provides autoconf 2.59. diff --git a/Makefile.am b/Makefile.am index 8371041b..405142f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,18 +1,19 @@ ## Process this file with automake to produce Makefile.in. SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @BABELD@ \ - @ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ - redhat @SOLARIS@ @BUILD_TESTS@ + @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ + redhat @SOLARIS@ tests DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d babeld \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ - solaris + solaris pimd EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ vtysh/Makefile.in vtysh/Makefile.am \ - tools/mrlg.cgi tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ - tools/zebra.el tools/multiple-bgpd.sh + tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ + tools/zebra.el tools/multiple-bgpd.sh \ + fpm/fpm.h if HAVE_LATEX diff --git a/NEWS b/NEWS index d67d6646..8f9dd7a1 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,76 @@ Note: this file lists major user-visible changes only. +* Changes in Quagga 0.99.24 + +User-visible changes: +- [pimd] New daemon: pimd provides IPv4 PIM-SSM multicast routing. +- [bgpd] New feature: "next-hop-self all" to override nexthop on iBGP route + reflector setups. +- [bgpd] route-maps have a new action "set ipv6 next-hop peer-address" +- [bgpd] route-maps have a new action "set as-path prepend last-as" +- [bgpd] Update validity checking (particularly MP-BGP / IPv6 routes) was + touched up significantly. Please report possible bugs. +- [ripd] New feature: RIP for IPv4 now supports equal-cost multipath (ECMP) +- [zebra] Multicast RIB support has been extended. It still is IPv4 only. +- [zebra] "no link-detect" is now printed in configurations since it won't + be the default anymore soon. To retain current behaviour, re-save your + configuration after updating to 0.99.24. + +Distributor-visible changes: +- --enable-pimd is added to enable pimd. It is considered experimental, though + unless the distribution target is embedded systems with little flash, there + is no reason to not include it in packages. +- --disable-ipv6 no longer exists as an option. It's 2015, your C library + really needs to have IPv6 support by now. +- --disable-netlink no longer exists as an option. It didn't work anyway. +- --disable-solaris no longer exists as an option. It only controlled some + init scripts. +- --enable-isisd is now the default. +- mrlg.cgi is no longer included (it was severely outdated). It can be found + independently at http://mrlg.op-sec.us/ +- build on Linux with the musl C library should now work + +* Changes in Quagga 0.99.23 + +Known issues: +- [bgpd] setting an extcommunity in a route map on a route that already has + an extcommunity attribute will cause bgpd to crash. This issue will be + fixed in a followup minor release. + +User-visible changes: +- [lib] Performance enhancements on hashes and timers. +- [bgpd] New feature: iBGP TTL security. +- [bgpd] New feature: relaxed bestpath criteria for multipath and improved + display of multipath routes in "show ip bgp". Scripts parsing this output + may need to be updated. +- [bgpd] Multiprotocol peerings over IPv6 now try to find a more appropriate + IPv4 nexthop by looking at the interface. +- [ospf6d] A large amount of changes has been merged for ospf6d. Careful + evaluation prior to deployment is recommended. +- [zebra] Recursive route support has been overhauled. Scripts parsing + "show ip route" output may need adaptation. +- [zebra] IPv6 address management has been improved regarding tentative + addresses. This is visible in that a freshly configured address will not + immediately be marked as usable. +- [*] a lot of bugs have been fixed, please refer to the git log + +* Changes in Quagga 0.99.22 + +- [bgpd] The semantics of default-originate route-map have changed. + The route-map is now used to advertise the default route conditionally. + The old behaviour which allowed to set attributes on the originated + default route is no longer supported. +- [bgpd] There is now a replace-as option to neighbor ... local-as ... + no-prepend. For details, refer to the user documentation. +- [zebra] An FPM interface has been added. This provides an alternate + interface to routing information and is geared at OpenFlow & co. +- [snmp] AgentX is now supported; the old smux backend is considered + deprecated. ospf6d has also had OSPFV3-MIB added. +- [*] several issues with configuration save/load/apply have been fixed, + in particular on ospf "max-metric router-lsa administrative" and + "distribute-list", bgpd "no neighbor activate", isisd "metric-style", +- [*] a lot of bugs have been fixed, please refer to the git log + * Changes in Quagga 0.99.21 - [bgpd] BGP multipath support has been merged @@ -181,7 +252,7 @@ which the PtP patch introduced. * Chages in ospf6d ** Many bugs are fixed. - + * Changes in zebra-0.92a * Changes in bgpd @@ -198,7 +269,7 @@ which the PtP patch introduced. * Changes in zebra ** Treat kernel type routes as EGP routes. - + * Changes in zebra-0.92 ** Overall security is improved. Default umask is 0077. @@ -255,7 +326,7 @@ router bgp 7675 multiple IP address for an interface. ** Redistribution of loopback interface's address works fine. - + * Changes in zebra-0.91 ** --enable-oldrib configure option is removed. @@ -307,7 +378,7 @@ only supported on GNU/Linux with netlink interface. ** Fix bug of LSA MaxAge flood. ** Fix bug of NSSA codes. - + * Changes in zebra-0.90 ** From this beta release, --enable-unixdomain and --enable-newrib @@ -643,7 +714,7 @@ zebrastart.sh /usr/local/sbin/ospfd -d /usr/local/sbin/bgpd -d /usr/local/bin/vtysh -b - + * Changes in zebra-0.89 * Changes in lib @@ -737,7 +808,7 @@ value. it is available. ** Reflect IPv6 interface's address change to protocol daemons. - + * Changes in zebra-0.88 * Changes in lib @@ -858,7 +929,7 @@ generating MRT compatible dump output. * Changes in vtysh ** VTY shell is now included into the distribution. - + * Changes in zebra-0.87 * Changes in lib @@ -951,7 +1022,7 @@ bgp ASN. * Changes in zebra ** Better interface up/down event handle. - + * Changes in zebra-0.86 * Changes in lib @@ -1035,7 +1106,7 @@ fixed. ** Remove client structure when client dies. ** Take care static route when interface goes up/down. - + * Changes in zebra-0.85 * Changes in bgpd @@ -1058,7 +1129,7 @@ drastically improved. * Changes in ripd ** RIPv1 update is done by class-full manner. - + * Changes in zebra-0.84b * Changes in lib @@ -1077,14 +1148,14 @@ consume only one screen size memory. ** Fix debug output string. ** Add RIP peer handling. RIP peer are shown by "show ip protocols". - + * Changes in zebra-0.84a * Changes in bgpd ** Fix serious bug of BGP-4+ peering under IPv6 link-local address. Due to the bug BGP-4+ peering may not be established. - + * Changes in zebra-0.84 * Changes in lib @@ -1163,7 +1234,7 @@ consume only one screen size memory. this command, you have to configure neighbor with "neighbor A.B.C.D soft-reconfiguration inbound" beforehand. - + * Changes in zebra-0.83 * bgpd @@ -1175,7 +1246,7 @@ introduced in zebra-0.82. ** When bgpd send Notify message, don't use thread manager. It is now send to neighbor immediately. - + * Changes in zebra-0.82 ** Solaris 2.6 support is added by Michael Handler @@ -1226,7 +1297,7 @@ draft-ietf-idr-bgp4-cap-neg-04.txt. * Changes in ospf6d ** Many debug feature is added. - + * Changes in zebra-0.81 ** SNMP support is disabled in default.--enable-snmp option is added @@ -1235,7 +1306,7 @@ to configure script. * Changes in bgpd ** Fix FSM bug which introduced in zebra-0.80. - + * Changes in zebra-0.80 * access-list @@ -1498,7 +1569,7 @@ to configure script. From zebra-0.80, ripd will reload it's configuration file when ripd receives HUP signal. Other daemon such as bgpd, ospfd will support HUP signal treatment soon. - + * Changes in zebra-0.79 * Changes in zebra @@ -1534,7 +1605,7 @@ not work for NetBSD-currnet on SparcStation 10. * Changes in ospf6d ** Enclosed KAME specific part with #ifdef #endif - + * Changes in zebra-0.78 * Changes in lib @@ -1579,7 +1650,7 @@ RIP_METRIC_INFINITY with network byte order using htonl (). * Changes in ospf6d ** `ip6' statement in configuration is changed to `ipv6'. - + * Changes in zebra-0.77 * Changes in lib @@ -1650,7 +1721,7 @@ timeout, garbage timer. * Changes in ospf6d ** Redistribute route works. - + * Changes in zebra-0.76 * Changes in lib @@ -1660,7 +1731,7 @@ timeout, garbage timer. ** Include SERVICES file to the distribution ** Update zebra.texi to zebra-0.76. - + * Changes in zebra-0.75 * Changes in lib @@ -1707,7 +1778,7 @@ used when `next-hop-self'. ** Never include a neighbor in Hello packet, when the neighbor goes down. - + * Changes in zebra-0.74 * Changes in lib @@ -1768,7 +1839,7 @@ router. ** LSA data structure is changed. ** Call of log_rotate() is removed. - + * Changes in zebra-0.73 * Changes in lib @@ -1830,7 +1901,7 @@ DEFAULT_LOCAL_PREF(100). ** Clean up logging message. ** Reflect routing information to zebra daemon. - + * Changes in zebra-0.72 * Changes in lib @@ -1851,7 +1922,7 @@ set ipv6 nexthop local -> set ipv6 next-hop local * Changes in ospfd ** Fix bug of multiple `network area' directive crashes. - + * Changes in zebra-0.71 * Changes in lib @@ -1902,7 +1973,7 @@ NOTIFY Malformed AS path is send to the peer. * Chanegs in ospf6d ** Routing table code is rewritten. - + * Changes in zebra-0.70 * Changes in zebra @@ -1923,7 +1994,7 @@ nexthop is calculated by looking up IGP routing table. * Changes in ospfd ** DD null pointer bug is fixed. - + * Changes in zebra-0.69 * Changes in zebra @@ -1973,7 +2044,7 @@ peer. * Changes in ospfd ** LS request and LS update can be send and received. - + * Changes in zebra-0.68 * Changes in lib @@ -2006,7 +2077,7 @@ summary-only mode. * Changes in ospf6d ** router zebra related bug is fixed. - + * Changes in zebra-0.67 * Changes in lib @@ -2048,7 +2119,7 @@ it is fixed. * Changes in ospf6d ** `router zebra' is default behavior. - + * Changes in zebra-0.66 * Changes in zebra @@ -2092,7 +2163,7 @@ routes to the kernel, please configure like below: router zebra no redistribute ripng ! - + * Changes in zebra-0.65 * Changes in lib @@ -2144,7 +2215,7 @@ zebra. * Changes in ospf6d ** Bug fix about network vertex. - + * Changes in zebra-0.64.1. This is bug fix release. @@ -2182,7 +2253,7 @@ not work with previous version, sorry. ** Fix bug of no network IPV6_NETWORK. ** Important bug fix about intra-area-prefix-lsa. - + * Changes in zebra-0.64. * Changes in lib @@ -2221,7 +2292,7 @@ Barcenilla. ** There are many changes. If you have interested in ospf6d please visit ospf6d/README file. - + * Changes in zebra-0.63 first beta package. * Changes in lib @@ -2240,7 +2311,7 @@ visit ospf6d/README file. * Changes in ospf6d ** Now ospf6d can be compiled on both Linux and *BSD system. - + * Changes in zebra-19990420 snapshot ** `make dist' at top directory works now. @@ -2283,7 +2354,7 @@ router bgp ASN ** configure --enable-guile turns on zebra-guile build. ** (router-bgp ASN) allocates real bgp structre. - + * Changes in zebra-19990416 snapshot ** Set version to 0.60 for preparation of beta release. @@ -2314,7 +2385,7 @@ Bligh . * Changes in ospfd ** DR and BDR information is shown by `show ip ospf interface' command. - + * Changes in zebra-19990408 snapshot * Changes in bgpd @@ -2341,7 +2412,7 @@ kad@gibson.skif.net. ** With KAME stack, terminal interface is now bind AF_INET socket instead of AF_INET6 one. - + * Changes in zebra-19990403 snapshot * Changes in bgpd @@ -2360,7 +2431,7 @@ segment. This change is for announcement to gated under iBGP. ** Yasuhiro Ohara's ospf6d codes is imported. It is under development and can't be compiled on any platform. - + * Changes in zebra-19990327 snapshot * Changes in bgpd @@ -2405,7 +2476,7 @@ buffer when the address family is not supported and the length is big * Changes in ospfd ** Now ospfd receive OSPF packet. - + * Changes in zebra-19990319 snapshot * Changes in configuration and libraries @@ -2496,8 +2567,8 @@ several files are included in ospfd directory. ** ospf6d codes are merged from Yasuhiro Ohara 's ospfd work. Now codes are located in ospf6d directory. - + Local variables: mode: outline -paragraph-separate: "[ ]*$" +paragraph-separate: "[ ]*$" end: diff --git a/SERVICES b/SERVICES index 9c3546bc..c69d0c1a 100644 --- a/SERVICES +++ b/SERVICES @@ -17,3 +17,4 @@ bgpd 2605/tcp ospf6d 2606/tcp ospfapi 2607/tcp isisd 2608/tcp +pimd 2611/tcp diff --git a/TODO b/TODO index 24941de0..246cc257 100644 --- a/TODO +++ b/TODO @@ -1,78 +1,209 @@ Quagga TODO list - 2004/11/24 + 2013-03-29 -zebra: -o Pointopoint address configuration. -o Multiple (alias) address configuration for the interface when kernel - support it [just starting]. -o improve rtnetlink to handle sequence number tracking and reconciliation - and resyncs. -o Add support for valid and preferred lifetimes to IPv6 addresses -o proper support for (at least) 1-level recursive routes -o Ability to set src on routes, where systems support it. -o Ability to apply route-maps to daemon route updates. +This is the Quagga primary TODO list. It is on git because that way changes +pass through the usual process just like code does, therefore they will have +the same visibility. -bgpd: +If you are working on something beyond a simple fix, to avoid double work it +is a good idea to submit a patch to this TODO list when you are starting, +listing what you're doing. Also, as others may have done just that, check +the list before starting. -o BGP TCP MD5 authentication by password command. -o HUP signal support (reload configuration file). -o BGP multi-path extension -o move FSM state to be per-connection, not per-peer. -o Add support for internal and minimum-metric MED setting +Google Summer of Code 2013 note: this list double-serves as idea list for the +Summer of Code. Ideas considered suitable for students are marked with a star +after the number, like this: "[Q999*] achieve world peace". They will also +have extended descriptions. Nevertheless, if you'd like to do something else, +just write a mail to the mailing list: quagga-dev@lists.quagga.net -ripd: +"GSoC-Mentors:" listings are preliminary at this point. -o Multipath support. + +Overall +======= + +[Q000] improve unit test architecture + +[Q001] kick invalid runtime tests from configure.ac, use list of supported + OSes and their APIs instead. + Priority: low + State: patch half-done 2013-03-29 David Lamparter + +[Q002*] clean up zebra IPC, remove code duplication, align to common API + Priority: high + GSoC-Mentors: David Lamparter, Christian Franke + + Quagga posesses an IPC mechanism to exchange route information among + the different daemons and Zebra, the kernel-interface. This mechanism + is implemented in libzebra, but is currently used in all sorts of + different ways in the individual protocol daemons. Also, in the future + the entire protocol needs to be redone in an extensible way, so we're + able to support MPLS, BFD, Multi-Topology/Instance, VRFs, ... + + This TODO entry only refers to the first-step API cleanup. All the + daemons need to use a single, well-defined libzebra API. Only after + this has been addressed can we look upon changing the protocol itself, + since by then it will be encapsulated inside libzebra. + +[Q003] add multi-instance / multi-topology support to the individual protocols + +[Q004] MPLS support + State: work in progress 2013-03-29 Renato Westphal, Timo Teräs + +[Q005] BFD support + State: two old implementations exist, contact Hasso Tepper + + +library +======= + +[L000] improve route_table speed, eg strided lookups for common prefix depths. + +[L001] ipv6 addresses need concept of valid/preferred + +[L002] implement a generic daemon access/control protocol (eg D-Bus like? + simplified SNMP-a-like? NETCONF?) + +[L003] extend vty command definitions to allow them to be self-documenting + i18n command help strings + +[L004] create a common libspf (for ospfd, ospf6d and possibly isisd and more). + cf. TODO item [O000] for the ospfd/ospf6d specific variant + +[L005] stabilise the API (possibly including symbol/library versioning voodoo) + +[L006] Document the exported API (DocBook/Doxygen?) + +[LE00] incorporate library changes from Euro-IX branch, except threading + +[LE01] incorporate threading library support from Euro-IX branch + + +zebra +===== + +[Z000] Pointopoint address configuration. + Priority: low + State: patch done & tested 2013-03-29 David Lamparter + +[Z001] Add support for valid and preferred lifetimes to IPv6 addresses + +[Z002] proper support for (at least) 1-level recursive routes + Priority: high + +[Z003] Ability to set src on routes, where systems support it. + +[Z004] Ability to apply route-maps to daemon route updates. + + +bgpd +==== + +[B000] HUP signal support (reload configuration file). + +[B001*] BGP multi-path extension, relaxed mode + Priority: medium + Implemented, patch will be sent shortly + Pradosh Mohapatra, Cumulus Networks + +[B002] move FSM state to be per-connection, not per-peer. + +[B003] Add support for internal and minimum-metric MED setting + + +ripd +==== + +[R000] Multipath support. + + +ospfd/ospf6d +============ + +[O000] move SPF to common code + +[O001] extend code sharing between ospfd and ospf6d beyond SPF + +[O002*] OSPF testing replay tool + Priority: medium + GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter + + In order to extensively test OSPF implementations, a tool to fake an + OSPF neighbor is immensely useful. This tool needs to be capable of + forming an adjacency and pushing LSAs to the device to be tested. To + maintain the adjacency, some minimal state tracking is useful. + + In total, the tool needs to form an adjacency, read and push LSAs, and + output received LSAs. Additional tools to generate LSAs from + specifications as well as verify received LSA correctness can then be + built on top of that. + + The tool needs to support IPv4 and IPv6, possibly split into 2 tools + with some code sharing. ospfd: -o Rewrite the incremental RT update code. -o Demand circuits. -o Multiple instances. -o OSPF MIB [SNMP get is amost finished]. -o HUP signal treatment. -o Fragment Oversized LSAs -o move SPF to common code -o NSSA priority rules (RFC3101 2.4) -o Type-7 address ranges (RFC3101 2.2) -o Originating Type-7 default into area (RFC3101 2.7) +[O400] Demand circuits. + Priority: very low + +[O401] Multiple instances. + Priority: medium + +[O402] HUP signal treatment. + Priority: medium + State: patch on ML needs review 2012-06-04 Mattias Walström ospf6d: -o move SPF to common code -o add router-id lookups - -isisd: - -o finish SPF -o select nearest L2 when running SPF for L1 -o remove the routes when holding time for nexthop expires -o redistribution -o autosummary - -o Mesh groups (RFC2973) -o Crypto authentication (RFC3567) - -lib: -o improve route_table speed, eg strided lookups for common prefix depths. -o improve hash tables, eg auto-growing hash tables -o move performance sensitive users of hashes over to jhash -o clean up linked lists -o ipv6 addresses need concept of valid/preferred -o implement a generic daemon access/control protocol (eg D-Bus like? - simplified SNMP-a-like?) -o merge SPF code from ospfd and ospf6d into a common libspf -o depends-on(generic A/C protocol) move snmp to seperate daemon -o extend command definitions to allow them to be self-documenting -o i18n command help strings -o Document the exported API (DocBook/Doxygen?) - -vtysh: -o untangle readline specific bits -o add a vtyd with a vty (ie telnet) frontend (as opposed to readline) -o depends-on(generic A/C protocol) use such -o better AAA support than just PAM, eg krb5, SASL, LDAP.. - ----------------------------- +[O600*] fix ospf6d in general + Priority: high + State: patches tickling in from Cumulus Networks 2013-03-29 Dinesh Dutt + Implemented: p2p link support, ABR, Stub area/Totally Stubby area, + SPF throttling, Improving state machine to get performance/scale, + max-metric support, Improving ECMP to be > 4, Various other bug fixes + + +[O601*] OSPFv3 autoconfiguration, prefix assignment and sourcedest routing + Priority: medium + State: work in progress 2013-03-29 Edward Seabrook + GSoC-Mentors: David Lamparter + + OSPFv3 application in the homenet is being designed to use several + extensions to the base protocol. In order of dependency, + autoconfiguration, prefix assignment and sourcedest routing should + be implemented. + + This task requires a good level of OSPF understanding plus proper + ability to follow IETF discussion about these points. Also, since work + has already started on this, improvements must obviously build on top + of that. + +isisd +===== + +[I000] reassess isisd TODO + +[I001*] IS-IS testing replay tool + Priority: medium + GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter + + see [O002*]. + +[I002] Mesh groups (RFC2973) + +[I003] Crypto authentication (RFC3567) + + +vtysh +===== + +[V000] untangle readline specific bits + +[V001] add a vtyd with a vty (ie telnet) frontend (as opposed to readline) + +[V002] (=> [L002]) use daemon control protocol + +[V003] better AAA support than just PAM, eg krb5, SASL, LDAP... diff --git a/babeld/Makefile.am b/babeld/Makefile.am index af1201a7..13922d0f 100644 --- a/babeld/Makefile.am +++ b/babeld/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/babeld/babel_main.c b/babeld/babel_main.c index 7b1d8799..529c3f29 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -277,9 +277,6 @@ babel_init(int argc, char **argv) /* this replace kernel_setup && kernel_setup_socket */ babelz_zebra_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get zebra configuration file. */ zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); vty_read_config (babel_config_file, babel_config_default); diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 9928734e..42f1f488 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 666218fa..e0fa58d4 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_packet.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_mplsvpn.h" - + /* BGP advertise attribute is used for pack same attribute update into one packet. To do that we maintain attribute hash in struct peer. */ @@ -79,7 +79,7 @@ baa_hash_cmp (const void *p1, const void *p2) return attrhash_cmp (baa1->attr, baa2->attr); } - + /* BGP update and withdraw information is stored in BGP advertise structure. This structure is referred from BGP adjacency information. */ @@ -151,7 +151,7 @@ bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa) baa_free (baa); } } - + /* BGP adjacency keeps minimal advertisement information. */ static void bgp_adj_out_free (struct bgp_adj_out *adj) @@ -327,7 +327,7 @@ bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, BGP_ADJ_OUT_DEL (rn, adj); bgp_adj_out_free (adj); } - + void bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr) { @@ -376,7 +376,7 @@ bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) bgp_adj_in_remove (rn, adj); bgp_unlock_node (rn); } - + void bgp_sync_init (struct peer *peer) { diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 4ebde907..2cf2a29b 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -21,13 +21,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_ADVERTISE_H #define _QUAGGA_BGP_ADVERTISE_H -/* BGP advertise FIFO. */ -struct bgp_advertise_fifo -{ - struct bgp_advertise *next; - struct bgp_advertise *prev; -}; - /* BGP advertise attribute. */ struct bgp_advertise_attr { @@ -44,7 +37,7 @@ struct bgp_advertise_attr struct bgp_advertise { /* FIFO for advertisement. */ - struct bgp_advertise_fifo fifo; + struct fifo fifo; /* Link list for same attribute advertise. */ struct bgp_advertise *next; @@ -97,11 +90,13 @@ struct bgp_adj_in /* BGP advertisement list. */ struct bgp_synchronize { - struct bgp_advertise_fifo update; - struct bgp_advertise_fifo withdraw; - struct bgp_advertise_fifo withdraw_low; + struct fifo update; + struct fifo withdraw; + struct fifo withdraw_low; }; +#define BGP_ADV_FIFO_HEAD(F) ((struct bgp_advertise *)FIFO_HEAD(F)) + /* BGP adjacency linked list. */ #define BGP_INFO_ADD(N,A,TYPE) \ do { \ diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index c37a8897..cfa9bc14 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -34,7 +34,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_attr.h" - + /* Attr. Flags and Attr. Type Code. */ #define AS_HEADER_SIZE 2 @@ -90,7 +90,7 @@ static struct hash *ashash; /* Stream for SNMP. See aspath_snmp_pathseg */ static struct stream *snmp_stream; - + /* Callers are required to initialize the memory */ static as_t * assegment_data_new (int num) @@ -308,7 +308,7 @@ assegment_normalise (struct assegment *head) } return head; } - + static struct aspath * aspath_new (void) { @@ -476,6 +476,19 @@ aspath_highest (struct aspath *aspath) return highest; } +/* Return the left-most ASN in path */ +as_t +aspath_leftmost (struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + as_t leftmost = 0; + + if (seg && seg->length && seg->type == AS_SEQUENCE) + leftmost = seg->as[0]; + + return leftmost; +} + /* Return 1 if there are any 4-byte ASes in the path */ unsigned int aspath_has_as4 (struct aspath *aspath) @@ -1362,46 +1375,50 @@ aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list) /* Add specified AS to the leftmost of aspath. */ static struct aspath * -aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type) +aspath_add_asns (struct aspath *aspath, as_t asno, u_char type, unsigned num) { struct assegment *assegment = aspath->segments; + int i; - /* In case of empty aspath. */ - if (assegment == NULL || assegment->length == 0) + if (assegment && assegment->type == type) { - aspath->segments = assegment_new (type, 1); - aspath->segments->as[0] = asno; - - if (assegment) - assegment_free (assegment); - - return aspath; + /* extend existing segment */ + aspath->segments = assegment_prepend_asns (aspath->segments, asno, num); } - - if (assegment->type == type) - aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1); else { - /* create new segment - * push it onto head of aspath's segment chain - */ - struct assegment *newsegment; - - newsegment = assegment_new (type, 1); - newsegment->as[0] = asno; - - newsegment->next = assegment; + /* prepend with new segment */ + struct assegment *newsegment = assegment_new (type, num); + for (i = 0; i < num; i++) + newsegment->as[i] = asno; + + /* insert potentially replacing empty segment */ + if (assegment && assegment->length == 0) + { + newsegment->next = assegment->next; + assegment_free (assegment); + } + else + newsegment->next = assegment; aspath->segments = newsegment; } + aspath_str_update (aspath); return aspath; } +/* Add specified AS to the leftmost of aspath num times. */ +struct aspath * +aspath_add_seq_n (struct aspath *aspath, as_t asno, unsigned num) +{ + return aspath_add_asns (aspath, asno, AS_SEQUENCE, num); +} + /* Add specified AS to the leftmost of aspath. */ struct aspath * aspath_add_seq (struct aspath *aspath, as_t asno) { - return aspath_add_one_as (aspath, asno, AS_SEQUENCE); + return aspath_add_asns (aspath, asno, AS_SEQUENCE, 1); } /* Compare leftmost AS value for MED check. If as1's leftmost AS and @@ -1600,7 +1617,7 @@ aspath_delete_confed_seq (struct aspath *aspath) struct aspath* aspath_add_confed_seq (struct aspath *aspath, as_t asno) { - return aspath_add_one_as (aspath, asno, AS_CONFED_SEQUENCE); + return aspath_add_asns (aspath, asno, AS_CONFED_SEQUENCE, 1); } /* Add new as value to as path structure. */ @@ -1657,7 +1674,7 @@ aspath_count (void) { return ashash->count; } - + /* Theoretically, one as path can have: @@ -1811,7 +1828,7 @@ aspath_str2aspath (const char *str) return aspath; } - + /* Make hash value by raw aspath data. */ unsigned int aspath_key_make (void *p) @@ -1856,7 +1873,7 @@ aspath_cmp (const void *arg1, const void *arg2) void aspath_init (void) { - ashash = hash_create_size (32767, aspath_key_make, aspath_cmp); + ashash = hash_create_size (32768, aspath_key_make, aspath_cmp); } void @@ -1868,7 +1885,7 @@ aspath_finish (void) if (snmp_stream) stream_free (snmp_stream); } - + /* return and as path value */ const char * aspath_print (struct aspath *as) diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index e8764cca..1311f8a5 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -71,6 +71,7 @@ extern struct aspath *aspath_dup (struct aspath *); extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *); extern struct aspath *aspath_prepend (struct aspath *, struct aspath *); extern struct aspath *aspath_filter_exclude (struct aspath *, struct aspath *); +extern struct aspath *aspath_add_seq_n (struct aspath *, as_t, unsigned); extern struct aspath *aspath_add_seq (struct aspath *, as_t); extern struct aspath *aspath_add_confed_seq (struct aspath *, as_t); extern int aspath_cmp (const void *, const void *); @@ -97,6 +98,7 @@ extern unsigned int aspath_count_hops (struct aspath *); extern unsigned int aspath_count_confeds (struct aspath *); extern unsigned int aspath_size (struct aspath *); extern as_t aspath_highest (struct aspath *); +extern as_t aspath_leftmost (struct aspath *); extern size_t aspath_put (struct stream *, struct aspath *, int); extern struct aspath *aspath_reconcile_as4 (struct aspath *, struct aspath *); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index cdc99ab3..be316daa 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -38,7 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_ecommunity.h" - + /* Attribute strings for logging. */ static const struct message attr_str [] = { @@ -73,7 +73,7 @@ static const struct message attr_flag_str[] = { BGP_ATTR_FLAG_EXTLEN, "Extended Length" }, }; static const size_t attr_flag_str_max = array_size(attr_flag_str); - + static struct hash *cluster_hash; static void * @@ -207,7 +207,7 @@ cluster_finish (void) hash_free (cluster_hash); cluster_hash = NULL; } - + /* Unknown transit attribute. */ static struct hash *transit_hash; @@ -283,7 +283,7 @@ transit_finish (void) hash_free (transit_hash); transit_hash = NULL; } - + /* Attribute hash routines. */ static struct hash *attrhash; @@ -321,11 +321,24 @@ bgp_attr_dup (struct attr *new, struct attr *orig) struct attr_extra *extra = new->extra; *new = *orig; - if (orig->extra) + /* if caller provided attr_extra space, use it in any case. + * + * This is neccesary even if orig->extra equals NULL, because otherwise + * memory may be later allocated on the heap by bgp_attr_extra_get. + * + * That memory would eventually be leaked, because the caller must not + * call bgp_attr_extra_free if he provided attr_extra on the stack. + */ + if (extra) + { + new->extra = extra; + memset(new->extra, 0, sizeof(struct attr_extra)); + if (orig->extra) + *new->extra = *orig->extra; + } + else if (orig->extra) { - /* if caller provided attr_extra space use it */ - if (! extra) - new->extra = bgp_attr_extra_new(); + new->extra = bgp_attr_extra_new(); *new->extra = *orig->extra; } } @@ -366,6 +379,7 @@ attrhash_key_make (void *p) MIX(extra->aggregator_addr.s_addr); MIX(extra->weight); MIX(extra->mp_nexthop_global_in.s_addr); + MIX(extra->originator_id.s_addr); } if (attr->aspath) @@ -421,7 +435,8 @@ attrhash_cmp (const void *p1, const void *p2) && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in) && ae1->ecommunity == ae2->ecommunity && ae1->cluster == ae2->cluster - && ae1->transit == ae2->transit) + && ae1->transit == ae2->transit + && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)) return 1; else if (ae1 || ae2) return 0; @@ -632,21 +647,21 @@ bgp_attr_unintern_sub (struct attr *attr) /* aspath refcount shoud be decrement. */ if (attr->aspath) aspath_unintern (&attr->aspath); - UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)); if (attr->community) community_unintern (&attr->community); - UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)); if (attr->extra) { if (attr->extra->ecommunity) ecommunity_unintern (&attr->extra->ecommunity); - UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)); if (attr->extra->cluster) cluster_unintern (attr->extra->cluster); - UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)); if (attr->extra->transit) transit_unintern (attr->extra->transit); @@ -779,7 +794,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode, return BGP_ATTR_PARSE_WITHDRAW; /* default to reset */ - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Find out what is wrong with the path attribute flag bits and log the error. @@ -1095,7 +1110,7 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args) if (IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h)) { char buf[INET_ADDRSTRLEN]; - inet_ntop (AF_INET, &nexthop_h, buf, INET_ADDRSTRLEN); + inet_ntop (AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN); zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf); return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, @@ -1267,7 +1282,17 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, int ignore_as4_path = 0; struct aspath *newpath; struct attr_extra *attre = attr->extra; - + + if (!attr->aspath) + { + /* NULL aspath shouldn't be possible as bgp_attr_parse should have + * checked that all well-known, mandatory attributes were present. + * + * Can only be a problem with peer itself - hard error + */ + return BGP_ATTR_PARSE_ERROR; + } + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) { /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR @@ -1347,9 +1372,9 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, /* need to reconcile NEW_AS_PATH and AS_PATH */ if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)))) { - newpath = aspath_reconcile_as4 (attr->aspath, as4_path); - aspath_unintern (&attr->aspath); - attr->aspath = aspath_intern (newpath); + newpath = aspath_reconcile_as4 (attr->aspath, as4_path); + aspath_unintern (&attr->aspath); + attr->aspath = aspath_intern (newpath); } return BGP_ATTR_PARSE_PROCEED; } @@ -1465,7 +1490,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s sent invalid length, %lu", __func__, peer->host, (unsigned long)length); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Load AFI, SAFI. */ @@ -1479,7 +1504,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Nexthop length check. */ @@ -1522,14 +1547,14 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, default: zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (!LEN_LEFT) { zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } { @@ -1545,7 +1570,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: (%s) Failed to read NLRI", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (safi != SAFI_MPLS_LABELED_VPN) @@ -1555,7 +1580,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: (%s) NLRI doesn't pass sanity check", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } } @@ -1566,6 +1591,8 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, stream_forward_getp (s, nlri_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; #undef LEN_LEFT } @@ -1581,13 +1608,14 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, u_int16_t withdraw_len; int ret; struct peer *const peer = args->peer; + struct attr *const attr = args->attr; const bgp_size_t length = args->length; s = peer->ibuf; #define BGP_MP_UNREACH_MIN_SIZE 3 if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE)) - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; afi = stream_getw (s); safi = stream_getc (s); @@ -1598,7 +1626,7 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, { ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len); if (ret < 0) - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } mp_withdraw->afi = afi; @@ -1608,6 +1636,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, stream_forward_getp (s, withdraw_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; } @@ -1708,8 +1738,58 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* Well-known attribute check. */ +static int +bgp_attr_check (struct peer *peer, struct attr *attr) +{ + u_char type = 0; + + /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an + * empty UPDATE. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag) + return BGP_ATTR_PARSE_PROCEED; + + /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required + to carry any other path attributes.", though if MP_REACH_NLRI or NLRI + are present, it should. Check for any other attribute being present + instead. + */ + if (attr->flag == ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI)) + return BGP_ATTR_PARSE_PROCEED; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) + type = BGP_ATTR_ORIGIN; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) + type = BGP_ATTR_AS_PATH; + + /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and + * NLRI is empty. We can't easily check NLRI empty here though. + */ + if (!CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) + && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI))) + type = BGP_ATTR_NEXT_HOP; + + if (peer->sort == BGP_PEER_IBGP + && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + type = BGP_ATTR_LOCAL_PREF; + + if (type) + { + zlog (peer->log, LOG_WARNING, + "%s Missing well-known attribute %d / %s", + peer->host, type, LOOKUP (attr_str, type)); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MISS_ATTR, + &type, 1); + return BGP_ATTR_PARSE_ERROR; + } + return BGP_ATTR_PARSE_PROCEED; +} + /* Read attribute of update packet. This function is called from - bgp_update() in bgpd.c. */ + bgp_update_receive() in bgp_packet.c. */ bgp_attr_parse_ret_t bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw) @@ -1895,6 +1975,14 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, break; } + if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS) + { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + ret = BGP_ATTR_PARSE_ERROR; + } + /* If hard error occured immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { @@ -1902,9 +1990,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, "%s: Attribute %s, parse error", peer->host, LOOKUP (attr_str, type)); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return ret; @@ -1935,7 +2020,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return BGP_ATTR_PARSE_ERROR; } } - /* Check final read pointer is same as end pointer. */ if (BGP_INPUT_PNT (peer) != endp) { @@ -1949,7 +2033,18 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; } - + + /* Check all mandatory well-known attributes are present */ + { + bgp_attr_parse_ret_t ret; + if ((ret = bgp_attr_check (peer, attr)) < 0) + { + if (as4_path) + aspath_unintern (&as4_path); + return ret; + } + } + /* * At this place we can see whether we got AS4_PATH and/or * AS4_AGGREGATOR from a 16Bit peer and act accordingly. @@ -1960,10 +2055,19 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, * So, to be defensive, we are not relying on any order and read * all attributes first, including these 32bit ones, and now, * afterwards, we look what and if something is to be done for as4. + * + * It is possible to not have AS_PATH, e.g. GR EoR and sole + * MP_UNREACH_NLRI. */ - if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, + /* actually... this doesn't ever return failure currently, but + * better safe than sorry */ + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)) + && bgp_attr_munge_as4_attrs (peer, attr, as4_path, as4_aggregator, &as4_aggregator_addr)) { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; @@ -2005,47 +2109,107 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return BGP_ATTR_PARSE_PROCEED; } -/* Well-known attribute check. */ -int -bgp_attr_check (struct peer *peer, struct attr *attr) +int stream_put_prefix (struct stream *, struct prefix *); + +size_t +bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, + struct attr *attr) { - u_char type = 0; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) - type = BGP_ATTR_ORIGIN; + size_t sizep; - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) - type = BGP_ATTR_AS_PATH; + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_MP_REACH_NLRI); + sizep = stream_get_endp (s); + stream_putw (s, 0); /* Marker: Attribute length. */ + stream_putw (s, afi); /* AFI */ + stream_putc (s, safi); /* SAFI */ - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) - type = BGP_ATTR_NEXT_HOP; + /* Nexthop */ + switch (afi) + { + case AFI_IP: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + stream_putc (s, 4); + stream_put_ipv4 (s, attr->nexthop.s_addr); + break; + case SAFI_MPLS_VPN: + stream_putc (s, 12); + stream_putl (s, 0); + stream_putl (s, 0); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + default: + break; + } + break; +#ifdef HAVE_IPV6 + case AFI_IP6: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + { + struct attr_extra *attre = attr->extra; - if (peer->sort == BGP_PEER_IBGP - && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) - type = BGP_ATTR_LOCAL_PREF; + assert (attr->extra); + stream_putc (s, attre->mp_nexthop_len); + stream_put (s, &attre->mp_nexthop_global, 16); + if (attre->mp_nexthop_len == 32) + stream_put (s, &attre->mp_nexthop_local, 16); + } + default: + break; + } + break; +#endif /*HAVE_IPV6*/ + default: + break; + } - if (type) + /* SNPA */ + stream_putc (s, 0); + return sizep; +} + +void +bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, + struct prefix *p, struct prefix_rd *prd, + u_char *tag) +{ + switch (safi) { - zlog (peer->log, LOG_WARNING, - "%s Missing well-known attribute %d.", - peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, - &type, 1); - return BGP_ATTR_PARSE_ERROR; + case SAFI_MPLS_VPN: + /* Tag, RD, Prefix write. */ + stream_putc (s, p->prefixlen + 88); + stream_put (s, tag, 3); + stream_put (s, prd->val, 8); + stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); + break; + default: + /* Prefix write. */ + stream_put_prefix (s, p); + break; } - return BGP_ATTR_PARSE_PROCEED; } - -int stream_put_prefix (struct stream *, struct prefix *); + +void +bgp_packet_mpattr_end (struct stream *s, size_t sizep) +{ + /* Set MP attribute length. Don't count the (2) bytes used to encode + the attr length */ + stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2); +} /* Make attribute packet. */ bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *peer, - struct stream *s, struct attr *attr, struct prefix *p, - afi_t afi, safi_t safi, struct peer *from, - struct prefix_rd *prd, u_char *tag) + struct stream *s, struct attr *attr, + struct prefix *p, afi_t afi, safi_t safi, + struct peer *from, struct prefix_rd *prd, u_char *tag) { size_t cp; size_t aspath_sizep; @@ -2053,6 +2217,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, int send_as4_path = 0; int send_as4_aggregator = 0; int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0; + size_t mpattrlen_pos = 0; if (! bgp) bgp = bgp_get_default (); @@ -2060,6 +2225,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, /* Remember current pointer. */ cp = stream_get_endp (s); + if (p && !(afi == AFI_IP && safi == SAFI_UNICAST)) + { + mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag); + bgp_packet_mpattr_end(s, mpattrlen_pos); + } + /* Origin attribute. */ stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_ORIGIN); @@ -2085,9 +2257,16 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } else { - aspath = aspath_add_seq (aspath, peer->local_as); - if (peer->change_local_as) + if (peer->change_local_as) { + /* If replace-as is specified, we only use the change_local_as when + advertising routes. */ + if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) { + aspath = aspath_add_seq (aspath, peer->local_as); + } aspath = aspath_add_seq (aspath, peer->change_local_as); + } else { + aspath = aspath_add_seq (aspath, peer->local_as); + } } } else if (peer->sort == BGP_PEER_CONFED) @@ -2261,96 +2440,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } } -#ifdef HAVE_IPV6 - /* If p is IPv6 address put it into attribute. */ - if (p->family == AF_INET6) - { - unsigned long sizep; - struct attr_extra *attre = attr->extra; - - assert (attr->extra); - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute length. */ - stream_putw (s, AFI_IP6); /* AFI */ - stream_putc (s, safi); /* SAFI */ - - stream_putc (s, attre->mp_nexthop_len); - - if (attre->mp_nexthop_len == 16) - stream_put (s, &attre->mp_nexthop_global, 16); - else if (attre->mp_nexthop_len == 32) - { - stream_put (s, &attre->mp_nexthop_global, 16); - stream_put (s, &attre->mp_nexthop_local, 16); - } - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } -#endif /* HAVE_IPV6 */ - - if (p->family == AF_INET && safi == SAFI_MULTICAST) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute Length. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MULTICAST); /* SAFI */ - - stream_putc (s, 4); - stream_put_ipv4 (s, attr->nexthop.s_addr); - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - - if (p->family == AF_INET && safi == SAFI_MPLS_VPN) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); /* SAFI */ - - stream_putc (s, 12); - stream_putl (s, 0); - stream_putl (s, 0); - stream_put (s, &attr->extra->mp_nexthop_global_in, 4); - - /* SNPA */ - stream_putc (s, 0); - - /* Tag, RD, Prefix write. */ - stream_putc (s, p->prefixlen + 88); - stream_put (s, tag, 3); - stream_put (s, prd->val, 8); - stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - /* Extended Communities attribute. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES))) @@ -2472,50 +2561,49 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, return stream_get_endp (s) - cp; } -bgp_size_t -bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p, - afi_t afi, safi_t safi, struct prefix_rd *prd, - u_char *tag) +size_t +bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) { - unsigned long cp; unsigned long attrlen_pnt; - bgp_size_t size; - - cp = stream_get_endp (s); - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); attrlen_pnt = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ + stream_putw (s, 0); /* Length of this attribute. */ - stream_putw (s, family2afi (p->family)); + stream_putw (s, afi); + safi = (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi; + stream_putc (s, safi); + return attrlen_pnt; +} +void +bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag) +{ if (safi == SAFI_MPLS_VPN) { - /* SAFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); - - /* prefix. */ stream_putc (s, p->prefixlen + 88); stream_put (s, tag, 3); stream_put (s, prd->val, 8); stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); } else - { - /* SAFI */ - stream_putc (s, safi); - - /* prefix */ - stream_put_prefix (s, p); - } + stream_put_prefix (s, p); +} - /* Set MP attribute length. */ - size = stream_get_endp (s) - attrlen_pnt - 1; - stream_putc_at (s, attrlen_pnt, size); +void +bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt) +{ + bgp_size_t size; - return stream_get_endp (s) - cp; + /* Set MP attribute length. Don't count the (2) bytes used to encode + the attr length */ + size = stream_get_endp (s) - attrlen_pnt - 2; + stream_putw_at (s, attrlen_pnt, size); } /* Initialization of attribute. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index df87c863..b59fa8e4 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -136,6 +136,9 @@ typedef enum { BGP_ATTR_PARSE_PROCEED = 0, BGP_ATTR_PARSE_ERROR = -1, BGP_ATTR_PARSE_WITHDRAW = -2, + + /* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR */ + BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3, } bgp_attr_parse_ret_t; /* Prototypes. */ @@ -144,7 +147,6 @@ extern void bgp_attr_finish (void); extern bgp_attr_parse_ret_t bgp_attr_parse (struct peer *, struct attr *, bgp_size_t, struct bgp_nlri *, struct bgp_nlri *); -extern int bgp_attr_check (struct peer *, struct attr *); extern struct attr_extra *bgp_attr_extra_get (struct attr *); extern void bgp_attr_extra_free (struct attr *); extern void bgp_attr_dup (struct attr *, struct attr *); @@ -157,13 +159,11 @@ extern struct attr *bgp_attr_default_intern (u_char); extern struct attr *bgp_attr_aggregate_intern (struct bgp *, u_char, struct aspath *, struct community *, int as_set); -extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, - struct stream *, struct attr *, - struct prefix *, afi_t, safi_t, - struct peer *, struct prefix_rd *, u_char *); -extern bgp_size_t bgp_packet_withdraw (struct peer *peer, struct stream *s, - struct prefix *p, afi_t, safi_t, - struct prefix_rd *, u_char *); +extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, + struct stream *, struct attr *, + struct prefix *, afi_t, safi_t, + struct peer *, struct prefix_rd *, + u_char *); extern void bgp_dump_routes_attr (struct stream *, struct attr *, struct prefix *); extern int attrhash_cmp (const void *, const void *); @@ -194,4 +194,24 @@ extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args, extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, struct bgp_nlri *); +/** + * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes. + * Typical call sequence is to call _start(), followed by multiple _prefix(), + * one for each NLRI that needs to be encoded into the UPDATE message, and + * finally the _end() function. + */ +extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi, + struct attr *attr); +extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, + struct prefix *p, struct prefix_rd *prd, + u_char *tag); +extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep); + +extern size_t bgp_packet_mpunreach_start (struct stream *s, afi_t afi, + safi_t safi); +extern void bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag); +extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt); + #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 6c9976e3..b91ab81f 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -30,7 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" - + /* Lookup master structure for community-list or extcommunity-list. */ struct community_list_master * @@ -262,7 +262,7 @@ community_list_empty_p (struct community_list *list) { return (list->head == NULL && list->tail == NULL) ? 1 : 0; } - + /* Add community-list entry to the list. */ static void community_list_entry_add (struct community_list *list, @@ -329,7 +329,7 @@ community_list_entry_lookup (struct community_list *list, const void *arg, } return NULL; } - + /* Internal function to perform regular expression match for community attribute. */ static int @@ -590,7 +590,7 @@ community_list_dup_check (struct community_list *list, } return 0; } - + /* Set community-list. */ int community_list_set (struct community_list_handler *ch, diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index fc1bef88..1bd2dd84 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -78,7 +78,7 @@ community_del_val (struct community *com, u_int32_t *val) c = com->size -i -1; if (c > 0) - memcpy (com->val + i, com->val + (i + 1), c * sizeof (*val)); + memmove (com->val + i, com->val + (i + 1), c * sizeof (*val)); com->size--; diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 2820f17c..0ffafb7a 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -42,7 +42,7 @@ static struct bgp_damp_config *damp = &bgp_damp_cfg; used list. */ #define BGP_DAMP_LIST_ADD(N,A) BGP_INFO_ADD(N,A,no_reuse_list) #define BGP_DAMP_LIST_DEL(N,A) BGP_INFO_DEL(N,A,no_reuse_list) - + /* Calculate reuse list index by penalty value. */ static int bgp_reuse_index (int penalty) @@ -86,7 +86,7 @@ bgp_reuse_list_delete (struct bgp_damp_info *bdi) else damp->reuse_list[bdi->index] = bdi->next; } - + /* Return decayed penalty value. */ int bgp_damp_decay (time_t tdiff, int penalty) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index e3e3ec86..1d097697 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -243,31 +243,37 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, const char *direct) { const char *subcode_str; + const char *code_str; subcode_str = ""; + code_str = LOOKUP_DEF (bgp_notify_msg, bgp_notify->code, + "Unrecognized Error Code"); - switch (bgp_notify->code) + switch (bgp_notify->code) { case BGP_NOTIFY_HEADER_ERR: - subcode_str = LOOKUP (bgp_notify_head_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_head_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_OPEN_ERR: - subcode_str = LOOKUP (bgp_notify_open_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_open_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_UPDATE_ERR: - subcode_str = LOOKUP (bgp_notify_update_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_update_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_HOLD_ERR: - subcode_str = ""; break; case BGP_NOTIFY_FSM_ERR: - subcode_str = ""; break; case BGP_NOTIFY_CEASE: - subcode_str = LOOKUP (bgp_notify_cease_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_cease_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_CAPABILITY_ERR: - subcode_str = LOOKUP (bgp_notify_capability_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_capability_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; } @@ -275,18 +281,16 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, zlog_info ("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s", strcmp (direct, "received") == 0 ? "received from" : "sent to", peer->host, bgp_notify->code, bgp_notify->subcode, - LOOKUP (bgp_notify_msg, bgp_notify->code), - subcode_str, bgp_notify->length, + code_str, subcode_str, bgp_notify->length, bgp_notify->data ? bgp_notify->data : ""); else if (BGP_DEBUG (normal, NORMAL)) plog_debug (peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s", peer ? peer->host : "", direct, bgp_notify->code, bgp_notify->subcode, - LOOKUP (bgp_notify_msg, bgp_notify->code), - subcode_str, bgp_notify->length, + code_str, subcode_str, bgp_notify->length, bgp_notify->data ? bgp_notify->data : ""); } - + /* Debug option setting interface. */ unsigned long bgp_debug_option = 0; diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 77548601..a3c9526f 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -33,7 +33,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_dump.h" - + enum bgp_dump_type { BGP_DUMP_ALL, @@ -89,13 +89,12 @@ struct bgp_dump bgp_dump_routes; /* Dump whole BGP table is very heavy process. */ struct thread *t_bgp_dump_routes; - + /* Some define for BGP packet dump. */ static FILE * bgp_dump_open_file (struct bgp_dump *bgp_dump) { int ret; - int fd; time_t clock; struct tm *tm; char fullpath[MAXPATHLEN]; @@ -132,16 +131,6 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump) umask(oldumask); return NULL; } - else - { - fd = fileno(bgp_dump->fp); - ret = flock( fd, LOCK_EX ); - if (ret != 0) - { - zlog_warn ("bgp_dump_open_file: Unable to flock() file %s: %s", realpath, strerror (errno)); - } - } - umask(oldumask); return bgp_dump->fp; @@ -206,7 +195,6 @@ bgp_dump_set_size (struct stream *s, int type) static void bgp_dump_routes_index_table(struct bgp *bgp) { - int ret; struct peer *peer; struct listnode *node; uint16_t peerno = 0; @@ -278,11 +266,7 @@ bgp_dump_routes_index_table(struct bgp *bgp) bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_routes_index_table: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); fflush (bgp_dump_routes.fp); } @@ -291,7 +275,6 @@ bgp_dump_routes_index_table(struct bgp *bgp) static unsigned int bgp_dump_routes_func (int afi, int first_run, unsigned int seq) { - int ret; struct stream *obuf; struct bgp_info *info; struct bgp_node *rn; @@ -390,11 +373,8 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq) seq++; bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_routes_func: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); + } fflush (bgp_dump_routes.fp); @@ -484,7 +464,6 @@ bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) void bgp_dump_state (struct peer *peer, int status_old, int status_new) { - int ret; struct stream *obuf; /* If dump file pointer is disabled return immediately. */ @@ -505,11 +484,7 @@ bgp_dump_state (struct peer *peer, int status_old, int status_new) bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_state: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); fflush (bgp_dump_all.fp); } @@ -517,7 +492,6 @@ static void bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, struct stream *packet) { - int ret; struct stream *obuf; /* If dump file pointer is disabled return immediately. */ @@ -546,11 +520,7 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_packet_func: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp); fflush (bgp_dump->fp); } @@ -565,7 +535,7 @@ bgp_dump_packet (struct peer *peer, int type, struct stream *packet) if (type == BGP_MSG_UPDATE) bgp_dump_packet_func (&bgp_dump_updates, peer, packet); } - + static unsigned int bgp_dump_parse_time (const char *str) { @@ -875,7 +845,7 @@ config_write_bgp_dump (struct vty *vty) } return 0; } - + /* Initialize BGP packet dump functionality. */ void bgp_dump_init (void) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 5722425e..482e76b7 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -31,7 +31,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Hash of community attribute. */ static struct hash *ecomhash; - + /* Allocate a new ecommunities. */ static struct ecommunity * ecommunity_new (void) @@ -277,7 +277,7 @@ ecommunity_finish (void) hash_free (ecomhash); ecomhash = NULL; } - + /* Extended Communities token enum. */ enum ecommunity_token { @@ -688,7 +688,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix, + len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix, eas.as, eas.val ); str_pnt += len; first = 0; @@ -703,7 +703,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eas.val |= (*pnt++ << 8); eas.val |= (*pnt++); - len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix, + len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix, eas.as, eas.val); str_pnt += len; first = 0; @@ -715,7 +715,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix, + len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix, inet_ntoa (eip.ip), eip.val); str_pnt += len; first = 0; diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 8ee62b01..fd8ece65 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -84,7 +84,7 @@ struct as_list struct as_filter *head; struct as_filter *tail; }; - + /* ip as-path access-list 10 permit AS1. */ static struct as_list_master as_list_master = @@ -370,7 +370,7 @@ as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter) if (as_list_master.delete_hook) (*as_list_master.delete_hook) (); } - + static int as_filter_match (struct as_filter *asfilter, struct aspath *aspath) { @@ -412,7 +412,7 @@ as_list_delete_hook (void (*func) (void)) { as_list_master.delete_hook = func; } - + static int as_list_dup_check (struct as_list *aslist, struct as_filter *new) { diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index d9200457..112c34a1 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -43,7 +43,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - + /* BGP FSM (finite state machine) has three types of functions. Type one is thread functions. Type two is event functions. Type three is FSM functions. Timer functions are set by bgp_timer_set @@ -592,6 +592,32 @@ bgp_stop_with_error (struct peer *peer) return 0; } + +/* something went wrong, send notify and tear down */ +static int +bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code) +{ + /* Send notify to remote peer */ + bgp_notify_send (peer, code, sub_code); + + /* Sweep if it is temporary peer. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); + peer_delete (peer); + return -1; + } + + /* Clear start timer value to default. */ + peer->v_start = BGP_INIT_START_TIMER; + + /* bgp_stop needs to be invoked while in Established state */ + bgp_stop(peer); + + return 0; +} + + /* TCP connection open. Next we send open message to remote peer. And add read thread for reading open message. */ static int @@ -740,29 +766,26 @@ bgp_fsm_keepalive_expire (struct peer *peer) return 0; } +/* FSM error, unexpected event. This is error of BGP connection. So cut the + peer and change to Idle status. */ +static int +bgp_fsm_event_error (struct peer *peer) +{ + plog_err (peer->log, "%s [FSM] unexpected packet received in state %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + + return bgp_stop_with_notify (peer, BGP_NOTIFY_FSM_ERR, 0); +} + /* Hold timer expire. This is error of BGP connection. So cut the peer and change to Idle status. */ static int bgp_fsm_holdtime_expire (struct peer *peer) { if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, "%s [FSM] Hold timer expire", peer->host); - - /* Send notify to remote peer. */ - bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0); - - /* Sweep if it is temporary peer. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); - peer_delete (peer); - return -1; - } + plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host); - /* bgp_stop needs to be invoked while in Established state */ - bgp_stop(peer); - - return 0; + return bgp_stop_with_notify (peer, BGP_NOTIFY_HOLD_ERR, 0); } /* Status goes to Established. Send keepalive packet then make first @@ -905,7 +928,7 @@ bgp_ignore (struct peer *peer) zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host); return 0; } - + /* Finite State Machine structure */ static const struct { int (*func) (struct peer *); @@ -977,8 +1000,8 @@ static const struct { {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ + {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ }, diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1ff1ac95..5026b5ea 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -171,7 +171,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ void sighup (void) @@ -314,7 +314,7 @@ bgp_exit (int status) exit (status); } - + /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ int @@ -431,9 +431,6 @@ main (int argc, char **argv) /* BGP related initialization. */ bgp_init (); - /* Sort CLI commands. */ - sort_node (); - /* Parse config file. */ vty_read_config (config_file, config_default); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index b0cf2a98..3d2dadef 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -84,7 +84,6 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, struct prefix p; int psize; int prefixlen; - u_int32_t label; u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip; @@ -117,8 +116,9 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, zlog_err ("prefix length is less than 88: %d", prefixlen); return -1; } - - label = decode_label (pnt); + + /* XXX: Not doing anything with the label */ + decode_label (pnt); /* Copyr label to prefix. */ tagpnt = pnt;; diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 79d5d27d..d0da9fa8 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -47,7 +47,7 @@ struct bgp_listener union sockunion su; struct thread *thread; }; - + /* * Set MD5 key for the socket, for the given IPv4 peer address. * If the password is NULL or zero-length, the option will be disabled. @@ -122,7 +122,72 @@ bgp_md5_set (struct peer *peer) return ret; } - + +/* Update BGP socket send buffer size */ +static void +bgp_update_sock_send_buffer_size (int fd) +{ + int size = BGP_SOCKET_SNDBUF_SIZE; + int optval; + socklen_t optlen = sizeof(optval); + + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) + { + zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno)); + return; + } + if (optval < size) + { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) + { + zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno)); + } + } +} + +static void +bgp_set_socket_ttl (struct peer *peer, int bgp_sock) +{ + char buf[INET_ADDRSTRLEN]; + int ret; + + /* In case of peer is EBGP, we should set TTL for this connection. */ + if (!peer->gtsm_hops && (peer_sort (peer) == BGP_PEER_EBGP)) + { + ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, peer->ttl); + if (ret) + { + zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + } + else if (peer->gtsm_hops) + { + /* On Linux, setting minttl without setting ttl seems to mess with the + outgoing ttl. Therefore setting both. + */ + ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, MAXTTL); + if (ret) + { + zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock, + MAXTTL + 1 - peer->gtsm_hops); + if (ret) + { + zlog_err ("%s: Can't set MinTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + } +} + /* Accept bgp connection. */ static int bgp_accept (struct thread *thread) @@ -153,6 +218,9 @@ bgp_accept (struct thread *thread) } set_nonblocking (bgp_sock); + /* Set socket send buffer size */ + bgp_update_sock_send_buffer_size(bgp_sock); + if (BGP_DEBUG (events, EVENTS)) zlog_debug ("[Event] BGP connection from host %s", inet_sutop (&su, buf)); @@ -173,12 +241,7 @@ bgp_accept (struct thread *thread) return -1; } - /* In case of peer is EBGP, we should set TTL for this connection. */ - if (peer1->sort == BGP_PEER_EBGP) { - sockopt_ttl (peer1->su.sa.sa_family, bgp_sock, peer1->ttl); - if (peer1->gtsm_hops) - sockopt_minttl (peer1->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer1->gtsm_hops); - } + bgp_set_socket_ttl (peer1, bgp_sock); /* Make dummy peer until read Open packet. */ if (BGP_DEBUG (events, EVENTS)) @@ -193,8 +256,7 @@ bgp_accept (struct thread *thread) peer->fd = bgp_sock; peer->status = Active; peer->local_id = peer1->local_id; - peer->v_holdtime = peer1->v_holdtime; - peer->v_keepalive = peer1->v_keepalive; + peer->v_holdtime = BGP_LARGE_HOLDTIME; /* Make peer's address string. */ sockunion2str (&su, buf, SU_ADDRSTRLEN); @@ -306,12 +368,12 @@ bgp_connect (struct peer *peer) if (peer->fd < 0) return -1; - /* If we can get socket for the peer, adjest TTL and make connection. */ - if (peer->sort == BGP_PEER_EBGP) { - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); - if (peer->gtsm_hops) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - peer->gtsm_hops); - } + set_nonblocking (peer->fd); + + /* Set socket send buffer size */ + bgp_update_sock_send_buffer_size(peer->fd); + + bgp_set_socket_ttl (peer, peer->fd); sockopt_reuseaddr (peer->fd); sockopt_reuseport (peer->fd); @@ -425,7 +487,7 @@ bgp_listener (int sock, struct sockaddr *sa, socklen_t salen) } /* IPv6 supported version of BGP server socket setup. */ -#if defined (HAVE_IPV6) && ! defined (NRL) +#ifdef HAVE_IPV6 int bgp_socket (unsigned short port, const char *address) { @@ -525,7 +587,7 @@ bgp_socket (unsigned short port, const char *address) } return sock; } -#endif /* HAVE_IPV6 && !NRL */ +#endif /* HAVE_IPV6 */ void bgp_close (void) diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 5bf2e5ff..12768430 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_NETWORK_H #define _QUAGGA_BGP_NETWORK_H +#define BGP_SOCKET_SNDBUF_SIZE 65536 + extern int bgp_socket (unsigned short, const char *); extern void bgp_close (void); extern int bgp_connect (struct peer *); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 0e56d368..6218e670 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -45,7 +45,7 @@ struct bgp_nexthop_cache *zlookup_query (struct in_addr); #ifdef HAVE_IPV6 struct bgp_nexthop_cache *zlookup_query_ipv6 (struct in6_addr *); #endif /* HAVE_IPV6 */ - + /* Only one BGP scan thread are activated at the same time. */ static struct thread *bgp_scan_thread = NULL; @@ -68,7 +68,7 @@ static struct bgp_table *bgp_connected_table[AFI_MAX]; /* BGP nexthop lookup query client. */ struct zclient *zlookup = NULL; - + /* Add nexthop to the end of the list. */ static void bnc_nexthop_add (struct bgp_nexthop_cache *bnc, struct nexthop *nexthop) @@ -109,7 +109,7 @@ bnc_free (struct bgp_nexthop_cache *bnc) bnc_nexthop_free (bnc); XFREE (MTYPE_BGP_NEXTHOP_CACHE, bnc); } - + static int bgp_nexthop_same (struct nexthop *next1, struct nexthop *next2) { @@ -122,6 +122,11 @@ bgp_nexthop_same (struct nexthop *next1, struct nexthop *next2) if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4)) return 0; break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4) + || next1->ifindex != next2->ifindex) + return 0; + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: if (next1->ifindex != next2->ifindex) @@ -453,7 +458,8 @@ bgp_scan (afi_t afi, safi_t safi) changed = 0; metricchanged = 0; - if (bi->peer->sort == BGP_PEER_EBGP && bi->peer->ttl == 1) + if (bi->peer->sort == BGP_PEER_EBGP && bi->peer->ttl == 1 + && !CHECK_FLAG(bi->peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) valid = bgp_nexthop_onlink (afi, bi->attr); else valid = bgp_nexthop_lookup (afi, bi->peer, bi, @@ -506,6 +512,16 @@ bgp_scan (afi_t afi, safi_t safi) else if (afi == AFI_IP6) zlog_debug ("scanning IPv6 Unicast routing tables"); } + + /* Reevaluate default-originate route-maps and announce/withdraw + * default route if neccesary. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->status == Established + && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE) + && peer->default_rmap[afi][safi].name) + bgp_default_originate (peer, afi, safi, 0); + } } /* BGP scan thread. This thread check nexthop reachability. */ @@ -594,6 +610,10 @@ bgp_address_del (struct prefix *p) tmp.addr = p->u.prefix4; addr = hash_lookup (bgp_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + addr->refcnt--; if (addr->refcnt == 0) @@ -603,7 +623,7 @@ bgp_address_del (struct prefix *p) } } - + struct bgp_connected_ref { unsigned int refcnt; @@ -761,7 +781,7 @@ bgp_nexthop_self (struct attr *attr) return 0; } - + static struct bgp_nexthop_cache * zlookup_read (void) { @@ -769,9 +789,9 @@ zlookup_read (void) uint16_t length; u_char marker; u_char version; - uint16_t command; - int nbytes; - struct in_addr raddr; + uint16_t command __attribute__((unused)); + int nbytes __attribute__((unused)); + struct in_addr raddr __attribute__((unused)); uint32_t metric; int i; u_char nexthop_num; @@ -781,6 +801,7 @@ zlookup_read (void) s = zlookup->ibuf; stream_reset (s); + /* nbytes not being checked */ nbytes = stream_read (s, zlookup->sock, 2); length = stream_getw (s); @@ -794,9 +815,11 @@ zlookup_read (void) __func__, zlookup->sock, marker, version); return NULL; } - + + /* XXX: not checking command */ command = stream_getw (s); + /* XXX: not doing anything with raddr */ raddr.s_addr = stream_get_ipv4 (s); metric = stream_getl (s); nexthop_num = stream_getc (s); @@ -817,6 +840,10 @@ zlookup_read (void) case ZEBRA_NEXTHOP_IPV4: nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); + nexthop->ifindex = stream_getl (s); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: nexthop->ifindex = stream_getl (s); @@ -877,8 +904,6 @@ zlookup_read_ipv6 (void) struct stream *s; uint16_t length; u_char version, marker; - uint16_t command; - int nbytes; struct in6_addr raddr; uint32_t metric; int i; @@ -889,10 +914,11 @@ zlookup_read_ipv6 (void) s = zlookup->ibuf; stream_reset (s); - nbytes = stream_read (s, zlookup->sock, 2); + /* XXX: ignoring nbytes, see also zread_lookup */ + stream_read (s, zlookup->sock, 2); length = stream_getw (s); - nbytes = stream_read (s, zlookup->sock, length - 2); + stream_read (s, zlookup->sock, length - 2); marker = stream_getc (s); version = stream_getc (s); @@ -902,9 +928,11 @@ zlookup_read_ipv6 (void) __func__, zlookup->sock, marker, version); return NULL; } - - command = stream_getw (s); + /* XXX: ignoring command */ + stream_getw (s); + + /* XXX: not actually doing anything with raddr */ stream_get (&raddr, s, 16); metric = stream_getl (s); @@ -990,10 +1018,10 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, { struct stream *s; int ret; - u_int16_t length, command; + u_int16_t length, command __attribute__((unused)); u_char version, marker; - int nbytes; - struct in_addr addr; + int nbytes __attribute__((unused)); + struct in_addr addr __attribute__((unused)); struct in_addr nexthop; u_int32_t metric = 0; u_char nexthop_num; @@ -1039,6 +1067,7 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, stream_reset (s); /* Fetch length. */ + /* XXX: not using nbytes */ nbytes = stream_read (s, zlookup->sock, 2); length = stream_getw (s); @@ -1053,9 +1082,11 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, __func__, zlookup->sock, marker, version); return 0; } - + + /* XXX: not using command */ command = stream_getw (s); + /* XXX: not using addr */ addr.s_addr = stream_get_ipv4 (s); metric = stream_getl (s); nexthop_num = stream_getc (s); @@ -1069,14 +1100,20 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, { nexthop.s_addr = 0; nexthop_type = stream_getc (s); - if (nexthop_type == ZEBRA_NEXTHOP_IPV4) + switch (nexthop_type) { + case ZEBRA_NEXTHOP_IPV4: + nexthop.s_addr = stream_get_ipv4 (s); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: nexthop.s_addr = stream_get_ipv4 (s); - if (igpnexthop) - *igpnexthop = nexthop; + /* ifindex */ (void)stream_getl (s); + break; + default: + /* do nothing */ + break; } - else - *igpnexthop = nexthop; + *igpnexthop = nexthop; return 1; } @@ -1213,7 +1250,7 @@ bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer) return 0; } - + DEFUN (bgp_scan_time, bgp_scan_time_cmd, "bgp scan-time <5-60>", @@ -1289,6 +1326,10 @@ show_ip_bgp_scan_tables (struct vty *vty, const char detail) case NEXTHOP_TYPE_IPV4: vty_out (vty, " gate %s%s", inet_ntop (AF_INET, &bnc->nexthop[i].gate.ipv4, buf, INET6_ADDRSTRLEN), VTY_NEWLINE); break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " gate %s", inet_ntop (AF_INET, &bnc->nexthop[i].gate.ipv4, buf, INET6_ADDRSTRLEN)); + vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); + break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); break; diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index af711cc8..b9d6e93f 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -230,7 +230,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) } /* validate number field */ - if (sizeof (struct capability_orf_entry) + (entry.num * 2) != hdr->length) + if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length) { zlog_info ("%s ORF Capability entry length error," " Cap length %u, num %u", @@ -338,13 +338,13 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) { struct stream *s = BGP_INPUT (peer); u_int16_t restart_flag_time; - int restart_bit = 0; size_t end = stream_get_getp (s) + caphdr->length; SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV); restart_flag_time = stream_getw(s); if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT)) - restart_bit = 1; + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV); + UNSET_FLAG (restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -352,7 +352,9 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) { zlog_debug ("%s OPEN has Graceful Restart capability", peer->host); zlog_debug ("%s Peer has%srestarted. Restart Time : %d", - peer->host, restart_bit ? " " : " not ", + peer->host, + CHECK_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV) ? " " + : " not ", peer->v_gr_restart); } @@ -898,10 +900,11 @@ void bgp_open_capability (struct stream *s, struct peer *peer) { u_char len; - unsigned long cp; + unsigned long cp, capp, rcapp; afi_t afi; safi_t safi; as_t local_as; + u_int32_t restart_time; /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); @@ -1020,16 +1023,43 @@ bgp_open_capability (struct stream *s, struct peer *peer) stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); } - /* Graceful restart capability */ + /* Sending base graceful-restart capability irrespective of the config */ + SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV); + stream_putc (s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp (s); /* Set Capability Len Pointer */ + stream_putc (s, 0); /* Capability Length */ + stream_putc (s, CAPABILITY_CODE_RESTART); + rcapp = stream_get_endp (s); /* Set Restart Capability Len Pointer */ + stream_putc (s, 0); + restart_time = peer->bgp->restart_time; + if (peer->bgp->t_startup) + { + SET_FLAG (restart_time, RESTART_R_BIT); + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_ADV); + } + stream_putw (s, restart_time); + + /* Send address-family specific graceful-restart capability only when GR config + is present */ if (bgp_flag_check (peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV); - stream_putc (s, BGP_OPEN_OPT_CAP); - stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); - stream_putc (s, CAPABILITY_CODE_RESTART); - stream_putc (s, CAPABILITY_CODE_RESTART_LEN); - stream_putw (s, peer->bgp->restart_time); - } + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (peer->afc[afi][safi]) + { + stream_putw (s, afi); + stream_putc (s, safi); + stream_putc (s, 0); //Forwarding is not retained as of now. + } + } + + /* Total Graceful restart capability Len. */ + len = stream_get_endp (s) - rcapp - 1; + stream_putc_at (s, rcapp, len); + + /* Total Capability Len. */ + len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, len); /* Total Opt Parm Len. */ len = stream_get_endp (s) - cp - 1; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 2d62c8da..14fd6e51 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -49,7 +49,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" int stream_put_prefix (struct stream *, struct prefix *); - + /* Set up BGP packet marker and packet type. */ static int bgp_packet_set_marker (struct stream *s, u_char type) @@ -142,18 +142,23 @@ static struct stream * bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) { struct stream *s; + struct stream *snlri; struct bgp_adj_out *adj; struct bgp_advertise *adv; struct stream *packet; struct bgp_node *rn = NULL; struct bgp_info *binfo = NULL; bgp_size_t total_attr_len = 0; - unsigned long pos; + unsigned long attrlen_pos = 0; + size_t mpattrlen_pos = 0; + size_t mpattr_pos = 0; s = peer->work; stream_reset (s); + snlri = peer->scratch; + stream_reset (snlri); - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update); while (adv) { @@ -164,39 +169,61 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) binfo = adv->binfo; /* When remaining space can't include NLRI and it's length. */ - if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) + if (STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) <= + (BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen))) break; /* If packet is empty, set attribute. */ if (stream_empty (s)) { - struct prefix_rd *prd = NULL; - u_char *tag = NULL; struct peer *from = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; + if (binfo) - { - from = binfo->peer; - if (binfo->extra) - tag = binfo->extra->tag; - } - + from = binfo->peer; + + /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length, + * one byte message type. + */ bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - pos = stream_get_endp (s); + + /* 2: withdrawn routes length */ stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, + + /* 3: total attributes length - attrlen_pos stores the position */ + attrlen_pos = stream_get_endp (s); + stream_putw (s, 0); + + /* 4: if there is MP_REACH_NLRI attribute, that should be the first + * attribute, according to draft-ietf-idr-error-handling. Save the + * position. + */ + mpattr_pos = stream_get_endp(s); + + /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */ + total_attr_len = bgp_packet_attribute (NULL, peer, s, adv->baa->attr, - &rn->p, afi, safi, - from, prd, tag); - stream_putw_at (s, pos, total_attr_len); + NULL, afi, safi, + from, NULL, NULL); } if (afi == AFI_IP && safi == SAFI_UNICAST) stream_put_prefix (s, &rn->p); - + else + { + /* Encode the prefix in MP_REACH_NLRI attribute */ + struct prefix_rd *prd = NULL; + u_char *tag = NULL; + + if (rn->prn) + prd = (struct prefix_rd *) &rn->prn->p; + if (binfo && binfo->extra) + tag = binfo->extra->tag; + + if (stream_empty(snlri)) + mpattrlen_pos = bgp_packet_mpattr_start(snlri, afi, safi, + adv->baa->attr); + bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, tag); + } if (BGP_DEBUG (update, UPDATE_OUT)) { char buf[INET6_BUFSIZ]; @@ -216,18 +243,28 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) adj->attr = bgp_attr_intern (adv->baa->attr); adv = bgp_advertise_clean (peer, adj, afi, safi); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; } - + if (! stream_empty (s)) { - bgp_packet_set_size (s); - packet = stream_dup (s); + if (!stream_empty(snlri)) + { + bgp_packet_mpattr_end(snlri, mpattrlen_pos); + total_attr_len += stream_get_endp(snlri); + } + + /* set the total attribute length correctly */ + stream_putw_at (s, attrlen_pos, total_attr_len); + + if (!stream_empty(snlri)) + packet = stream_dupcat(s, snlri, mpattr_pos); + else + packet = stream_dup (s); + bgp_packet_set_size (packet); bgp_packet_add (peer, packet); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); stream_reset (s); + stream_reset (snlri); return packet; } return NULL; @@ -277,6 +314,15 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) } /* Make BGP withdraw packet. */ +/* For ipv4 unicast: + 16-octet marker | 2-octet length | 1-octet type | + 2-octet withdrawn route length | withdrawn prefixes | 2-octet attrlen (=0) +*/ +/* For other afi/safis: + 16-octet marker | 2-octet length | 1-octet type | + 2-octet withdrawn route length (=0) | 2-octet attrlen | + mp_unreach attr type | attr len | afi | safi | withdrawn prefixes +*/ static struct stream * bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) { @@ -285,44 +331,54 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) struct bgp_adj_out *adj; struct bgp_advertise *adv; struct bgp_node *rn; - unsigned long pos; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t attrlen_pos = 0; + size_t mplen_pos = 0; + u_char first_time = 1; s = peer->work; stream_reset (s); - while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) + while ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) { assert (adv->rn); adj = adv->adj; rn = adv->rn; - if (STREAM_REMAIN (s) + if (STREAM_REMAIN (s) < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) break; if (stream_empty (s)) { bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); + stream_putw (s, 0); /* unfeasible routes length */ } + else + first_time = 0; if (afi == AFI_IP && safi == SAFI_UNICAST) stream_put_prefix (s, &rn->p); else { struct prefix_rd *prd = NULL; - + if (rn->prn) prd = (struct prefix_rd *) &rn->prn->p; - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len - = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL); - - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); + + /* If first time, format the MP_UNREACH header */ + if (first_time) + { + attrlen_pos = stream_get_endp (s); + /* total attr length = 0 for now. reevaluate later */ + stream_putw (s, 0); + mp_start = stream_get_endp (s); + mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + } + + bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL); } if (BGP_DEBUG (update, UPDATE_OUT)) @@ -339,20 +395,26 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) bgp_adj_out_remove (rn, adj, peer, afi, safi); bgp_unlock_node (rn); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; } if (! stream_empty (s)) { if (afi == AFI_IP && safi == SAFI_UNICAST) { - unfeasible_len + unfeasible_len = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN; stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len); stream_putw (s, 0); } + else + { + /* Set the mp_unreach attr's length */ + bgp_packet_mpunreach_end(s, mplen_pos); + + /* Set total path attribute length. */ + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at (s, attrlen_pos, total_attr_len); + } bgp_packet_set_size (s); packet = stream_dup (s); bgp_packet_add (peer, packet); @@ -439,10 +501,12 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) struct stream *s; struct stream *packet; struct prefix p; - unsigned long pos; + unsigned long attrlen_pos = 0; unsigned long cp; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t mplen_pos = 0; if (DISABLE_BGP_ANNOUNCE) return; @@ -455,7 +519,6 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) #endif /* HAVE_IPV6 */ total_attr_len = 0; - pos = 0; if (BGP_DEBUG (update, UPDATE_OUT)) { @@ -490,12 +553,18 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) } else { - pos = stream_get_endp (s); + attrlen_pos = stream_get_endp (s); stream_putw (s, 0); - total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL); + mp_start = stream_get_endp (s); + mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + bgp_packet_mpunreach_prefix(s, &p, afi, safi, NULL, NULL); + + /* Set the mp_unreach attr's length */ + bgp_packet_mpunreach_end(s, mplen_pos); /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at (s, attrlen_pos, total_attr_len); } bgp_packet_set_size (s); @@ -525,7 +594,7 @@ bgp_write_packet (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw); if (adv) { s = bgp_withdraw_packet (peer, afi, safi); @@ -537,13 +606,17 @@ bgp_write_packet (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update); if (adv) { if (adv->binfo && adv->binfo->uptime < peer->synctime) { if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV) && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV) + && ! (CHECK_FLAG (adv->binfo->peer->cap, + PEER_CAP_RESTART_BIT_RCV) && + CHECK_FLAG (adv->binfo->peer->cap, + PEER_CAP_RESTART_BIT_ADV)) && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE) && safi != SAFI_MPLS_VPN) { @@ -593,7 +666,7 @@ bgp_write_proceed (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if ((adv = FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) + if ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) if (adv->binfo->uptime < peer->synctime) return 1; @@ -677,7 +750,8 @@ bgp_write (struct thread *thread) /* Flush any existing events */ BGP_EVENT_ADD (peer, BGP_Stop); - return 0; + goto done; + case BGP_MSG_KEEPALIVE: peer->keepalive_out++; break; @@ -698,9 +772,9 @@ bgp_write (struct thread *thread) if (bgp_write_proceed (peer)) BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - else - sockopt_cork (peer->fd, 0); - + + done: + sockopt_cork (peer->fd, 0); return 0; } @@ -718,17 +792,26 @@ bgp_write_notify (struct peer *peer) return 0; assert (stream_get_endp (s) >= BGP_HEADER_SIZE); - /* Put socket in blocking mode. */ - val = fcntl (peer->fd, F_GETFL, 0); - fcntl (peer->fd, F_SETFL, val & ~O_NONBLOCK); + /* Stop collecting data within the socket */ + sockopt_cork (peer->fd, 0); + + /* socket is in nonblocking mode, if we can't deliver the NOTIFY, well, + * we only care about getting a clean shutdown at this point. */ + ret = write (peer->fd, STREAM_DATA (s), stream_get_endp (s)); - ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); + /* only connection reset/close gets counted as TCP_fatal_error, failure + * to write the entire NOTIFY doesn't get different FSM treatment */ if (ret <= 0) { BGP_EVENT_ADD (peer, TCP_fatal_error); return 0; } + /* Disable Nagle, make NOTIFY packet go out right away */ + val = 1; + (void) setsockopt (peer->fd, IPPROTO_TCP, TCP_NODELAY, + (char *) &val, sizeof (val)); + /* Retrieve BGP packet type. */ stream_set_getp (s, BGP_MARKER_SIZE + 2); type = stream_getc (s); @@ -1086,7 +1169,7 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); } - + /* RFC1771 6.8 Connection collision detection. */ static int bgp_collision_detect (struct peer *new, struct in_addr remote_id) @@ -1411,14 +1494,16 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Peer BGP version check. */ if (version != BGP_VERSION_4) { - u_int8_t maxver = BGP_VERSION_4; + u_int16_t maxver = htons(BGP_VERSION_4); + /* XXX this reply may not be correct if version < 4 XXX */ if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s bad protocol version, remote requested %d, local request %d", peer->host, version, BGP_VERSION_4); + /* Data must be in network byte order here */ bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSUP_VERSION, - &maxver, 1); + (u_int8_t *) &maxver, 2); return -1; } @@ -1638,7 +1723,10 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, &mp_update, &mp_withdraw); if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) - return -1; + { + bgp_attr_unintern_sub (&attr); + return -1; + } } /* Logging the attribute. */ @@ -1691,18 +1779,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) bgp_nlri_parse (peer, NULL, &withdraw); if (update.length) - { - /* We check well-known attribute only for IPv4 unicast - update. */ - ret = bgp_attr_check (peer, &attr); - if (ret < 0) - { - bgp_attr_unintern_sub (&attr); - return -1; - } - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update); - } if (mp_update.length && mp_update.afi == AFI_IP @@ -1951,7 +2028,6 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) { afi_t afi; safi_t safi; - u_char reserved; struct stream *s; /* If peer does not have the capability, send notification. */ @@ -1979,7 +2055,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) /* Parse packet. */ afi = stream_getw (s); - reserved = stream_getc (s); + /* reserved byte */ + stream_getc (s); safi = stream_getc (s); if (BGP_DEBUG (normal, NORMAL)) @@ -2031,8 +2108,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD) { - u_char *p_pnt = stream_pnt (s); - u_char *p_end = stream_pnt (s) + orf_len; + uint8_t *p_pnt = stream_pnt (s); + uint8_t *p_end = stream_pnt (s) + orf_len; struct orf_prefix orfp; u_char common = 0; u_int32_t seq; @@ -2072,7 +2149,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) prefix_bgp_orf_remove_all (name); break; } - ok = ((p_end - p_pnt) >= sizeof(u_int32_t)) ; + ok = ((size_t)(p_end - p_pnt) >= sizeof(u_int32_t)) ; if (ok) { memcpy (&seq, p_pnt, sizeof (u_int32_t)); @@ -2124,8 +2201,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) ret = prefix_bgp_orf_set (name, afi, &orfp, (common & ORF_COMMON_PART_DENY ? 0 : 1 ), (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - - if (!ok || (ret != CMD_SUCCESS)) + + if (!ok || (ok && ret != CMD_SUCCESS)) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s Received misformatted prefixlist ORF." @@ -2161,11 +2238,9 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) struct capability_mp_data mpc; struct capability_header *hdr; u_char action; - struct bgp *bgp; afi_t afi; safi_t safi; - bgp = peer->bgp; end = pnt + length; while (pnt < end) @@ -2299,7 +2374,7 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size) /* Parse packet. */ return bgp_capability_msg_parse (peer, pnt, size); } - + /* BGP read utility function. */ static int bgp_read_packet (struct peer *peer) @@ -2383,6 +2458,15 @@ bgp_marker_all_one (struct stream *s, int length) return 1; } +/* Recent thread time. + On same clock base as bgp_clock (MONOTONIC) + but can be time of last context switch to bgp_read thread. */ +static time_t +bgp_recent_clock (void) +{ + return recent_relative_time().tv_sec; +} + /* Starting point of packet process function. */ int bgp_read (struct thread *thread) @@ -2511,14 +2595,14 @@ bgp_read (struct thread *thread) bgp_open_receive (peer, size); /* XXX return value ignored! */ break; case BGP_MSG_UPDATE: - peer->readtime = time(NULL); /* Last read timer reset */ + peer->readtime = bgp_recent_clock (); bgp_update_receive (peer, size); break; case BGP_MSG_NOTIFY: bgp_notify_receive (peer, size); break; case BGP_MSG_KEEPALIVE: - peer->readtime = time(NULL); /* Last read timer reset */ + peer->readtime = bgp_recent_clock (); bgp_keepalive_receive (peer, size); break; case BGP_MSG_ROUTE_REFRESH_NEW: diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 2412503d..e7357e54 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -59,7 +59,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; - + static struct bgp_node * bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p, struct prefix_rd *prd) @@ -89,7 +89,7 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix return rn; } - + /* Allocate bgp_info_extra */ static struct bgp_info_extra * bgp_info_extra_new (void) @@ -480,7 +480,20 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, /* 9. Maximum path check. */ if (newm == existm) { - if (new->peer->sort == BGP_PEER_IBGP) + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) + { + + /* + * For the two paths, all comparison steps till IGP metric + * have succeeded - including AS_PATH hop count. Since 'bgp + * bestpath as-path multipath-relax' knob is on, we don't need + * an exact match of AS_PATH. Thus, mark the paths are equal. + * That will trigger both these paths to get into the multipath + * array. + */ + *paths_eq = 1; + } + else if (new->peer->sort == BGP_PEER_IBGP) { if (aspath_cmp (new->attr->aspath, exist->attr->aspath)) *paths_eq = 1; @@ -670,7 +683,7 @@ bgp_cluster_filter (struct peer *peer, struct attr *attr) } return 0; } - + static int bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -700,15 +713,12 @@ bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, peer->rmap_type = 0; if (ret == RMAP_DENYMATCH) - { - /* Free newly generated AS path and community by route-map. */ - bgp_attr_flush (attr); - return RMAP_DENY; - } + /* caller has multiple error paths with bgp_attr_flush() */ + return RMAP_DENY; } return RMAP_PERMIT; } - + static int bgp_export_modifier (struct peer *rsclient, struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -780,7 +790,7 @@ bgp_import_modifier (struct peer *rsclient, struct peer *peer, } return RMAP_PERMIT; } - + static int bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -810,16 +820,6 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, if (from == peer) return 0; - /* If peer's id and route's nexthop are same. draft-ietf-idr-bgp4-23 5.1.3 */ - if (p->family == AF_INET - && IPV4_ADDR_SAME(&peer->remote_id, &riattr->nexthop)) - return 0; -#ifdef HAVE_IPV6 - if (p->family == AF_INET6 - && IPV6_ADDR_SAME(&peer->remote_id, &riattr->nexthop)) - return 0; -#endif - /* Aggregate-address suppress check. */ if (ri->extra && ri->extra->suppress) if (! UNSUPPRESS_MAP_NAME (filter)) @@ -952,6 +952,17 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, attr->local_pref = bgp->default_local_pref; } + /* If originator-id is not set and the route is to be reflected, + set the originator id */ + if (peer && from && peer->sort == BGP_PEER_IBGP && + from->sort == BGP_PEER_IBGP && + (! (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))) + { + attr->extra = bgp_attr_extra_get(attr); + IPV4_ADDR_COPY(&(attr->extra->originator_id), &(from->remote_id)); + SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID); + } + /* Remove MED if its an EBGP peer - will get overwritten by route-maps */ if (peer->sort == BGP_PEER_EBGP && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) @@ -962,7 +973,8 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* next-hop-set */ - if (transparent || reflect + if (transparent + || (reflect && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF_ALL)) || (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) && ((p->family == AF_INET && attr->nexthop.s_addr) #ifdef HAVE_IPV6 @@ -1695,6 +1707,7 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) break; } + SET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); return; } @@ -1856,7 +1869,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, break; /* AS path loop check. */ - if (aspath_loop_check (attr->aspath, rsclient->as) > peer->allowas_in[afi][safi]) + if (aspath_loop_check (attr->aspath, rsclient->as) > rsclient->allowas_in[afi][safi]) { reason = "as-path contains our own AS;"; goto filtered; @@ -2128,10 +2141,14 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, new_attr.extra = &new_extra; bgp_attr_dup (&new_attr, attr); - /* Apply incoming route-map. */ + /* Apply incoming route-map. + * NB: new_attr may now contain newly allocated values from route-map "set" + * commands, so we need bgp_attr_flush in the error paths, until we intern + * the attr (which takes over the memory references) */ if (bgp_input_modifier (peer, p, &new_attr, afi, safi) == RMAP_DENY) { reason = "route-map;"; + bgp_attr_flush (&new_attr); goto filtered; } @@ -2145,6 +2162,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) { reason = "non-connected next-hop;"; + bgp_attr_flush (&new_attr); goto filtered; } @@ -2155,6 +2173,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, || bgp_nexthop_self (&new_attr)) { reason = "martian next-hop;"; + bgp_attr_flush (&new_attr); goto filtered; } } @@ -2453,7 +2472,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, return 0; } - + void bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) { @@ -2461,8 +2480,9 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) struct attr attr; struct aspath *aspath; struct prefix p; - struct bgp_info binfo; struct peer *from; + struct bgp_node *rn; + struct bgp_info *ri; int ret = RMAP_DENYMATCH; if (!(afi == AFI_IP || afi == AFI_IP6)) @@ -2504,21 +2524,37 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) if (peer->default_rmap[afi][safi].name) { - binfo.peer = bgp->peer_self; - binfo.attr = &attr; - SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); - - ret = route_map_apply (peer->default_rmap[afi][safi].map, &p, - RMAP_BGP, &binfo); - + for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) + { + for (ri = rn->info; ri; ri = ri->next) + { + struct attr dummy_attr; + struct attr_extra dummy_extra; + struct bgp_info info; + + /* Provide dummy so the route-map can't modify the attributes */ + dummy_attr.extra = &dummy_extra; + bgp_attr_dup(&dummy_attr, ri->attr); + info.peer = ri->peer; + info.attr = &dummy_attr; + + ret = route_map_apply(peer->default_rmap[afi][safi].map, &rn->p, + RMAP_BGP, &info); + + /* The route map might have set attributes. If we don't flush them + * here, they will be leaked. */ + bgp_attr_flush(&dummy_attr); + if (ret != RMAP_DENYMATCH) + break; + } + if (ret != RMAP_DENYMATCH) + break; + } bgp->peer_self->rmap_type = 0; if (ret == RMAP_DENYMATCH) - { - bgp_attr_flush (&attr); - withdraw = 1; - } + withdraw = 1; } if (withdraw) @@ -2529,14 +2565,17 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) } else { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE); - bgp_default_update_send (peer, &attr, afi, safi, from); + if (! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE)) + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE); + bgp_default_update_send (peer, &attr, afi, safi, from); + } } bgp_attr_extra_free (&attr); aspath_unintern (&aspath); } - + static void bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table, int rsclient) @@ -2607,7 +2646,7 @@ bgp_announce_route_all (struct peer *peer) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) bgp_announce_route (peer, afi, safi); } - + static void bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, struct bgp_table *table, struct prefix_rd *prd) @@ -2622,10 +2661,10 @@ bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, for (ain = rn->adj_in; ain; ain = ain->next) { struct bgp_info *ri = rn->info; + u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL; bgp_update_rsclient (rsclient, afi, safi, ain->attr, ain->peer, - &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, - (bgp_info_extra_get (ri))->tag); + &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, tag); } } @@ -2651,7 +2690,7 @@ bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi) bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, table, &prd); } } - + static void bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table, struct prefix_rd *prd) @@ -2669,10 +2708,11 @@ bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi, if (ain->peer == peer) { struct bgp_info *ri = rn->info; + u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL; ret = bgp_update (peer, &rn->p, ain->attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - prd, (bgp_info_extra_get (ri))->tag, 1); + prd, tag, 1); if (ret < 0) { @@ -2708,7 +2748,7 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) bgp_soft_reconfig_table (peer, afi, safi, table, &prd); } } - + struct bgp_clear_node_queue { @@ -2810,9 +2850,6 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_info *ri; struct bgp_adj_in *ain; struct bgp_adj_out *aout; - - if (rn->info == NULL) - continue; /* XXX:TODO: This is suboptimal, every non-empty route_node is * queued for every clearing peer, regardless of whether it is @@ -2845,6 +2882,21 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, * this may actually be achievable. It doesn't seem to be a huge * problem at this time, */ + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_in_remove (rn, ain); + bgp_unlock_node (rn); + break; + } + for (aout = rn->adj_out; aout; aout = aout->next) + if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_out_remove (rn, aout, peer, afi, safi); + bgp_unlock_node (rn); + break; + } + for (ri = rn->info; ri; ri = ri->next) if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) { @@ -2860,21 +2912,6 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, work_queue_add (peer->clear_node_queue, cnq); break; } - - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - bgp_adj_in_remove (rn, ain); - bgp_unlock_node (rn); - break; - } - for (aout = rn->adj_out; aout; aout = aout->next) - if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - bgp_adj_out_remove (rn, aout, peer, afi, safi); - bgp_unlock_node (rn); - break; - } } return; } @@ -3005,7 +3042,7 @@ bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) } } } - + /* Delete all kernel routes. */ void bgp_cleanup_routes (void) @@ -3046,7 +3083,7 @@ bgp_reset (void) access_list_reset (); prefix_list_reset (); } - + /* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr value. */ int @@ -3209,7 +3246,7 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, } return 0; } - + static struct bgp_static * bgp_static_new (void) { @@ -3976,7 +4013,7 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, return CMD_SUCCESS; } - + DEFUN (bgp_network, bgp_network_cmd, "network A.B.C.D/M", @@ -4356,7 +4393,7 @@ ALIAS_DEPRECATED (bgp_network_mask_natural, "AS-Pathlimit TTL, in number of AS-Path hops\n") ALIAS_DEPRECATED (bgp_network_mask_natural_backdoor, bgp_network_mask_natural_backdoor_ttl_cmd, - "network A.B.C.D backdoor pathlimit (1-255>", + "network A.B.C.D backdoor pathlimit <1-255>", "Specify a network to announce via BGP\n" "Network number\n" "Specify a BGP backdoor route\n" @@ -4434,7 +4471,7 @@ ALIAS_DEPRECATED (no_ipv6_bgp_network, "AS-Path hopcount limit attribute\n" "AS-Pathlimit TTL, in number of AS-Path hops\n") #endif /* HAVE_IPV6 */ - + /* Aggreagete address: advertise-map Set condition to advertise attribute @@ -4488,20 +4525,11 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, struct aspath *asmerge = NULL; struct community *community = NULL; struct community *commerge = NULL; - struct in_addr nexthop; - u_int32_t med = 0; struct bgp_info *ri; struct bgp_info *new; int first = 1; unsigned long match = 0; - /* Record adding route's nexthop and med. */ - if (rinew) - { - nexthop = rinew->attr->nexthop; - med = rinew->attr->med; - } - /* ORIGIN attribute: If at least one route among routes that are aggregated has ORIGIN with the value INCOMPLETE, then the aggregated route must have the ORIGIN attribute with the value @@ -4529,11 +4557,7 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, continue; if (! rinew && first) - { - nexthop = ri->attr->nexthop; - med = ri->attr->med; - first = 0; - } + first = 0; #ifdef AGGREGATE_NEXTHOP_CHECK if (! IPV4_ADDR_SAME (&ri->attr->nexthop, &nexthop) @@ -5345,7 +5369,7 @@ ALIAS (no_ipv6_aggregate_address_summary_only, "Aggregate prefix\n" "Filter more specific routes from updates\n") #endif /* HAVE_IPV6 */ - + /* Redistribute route treatment. */ void bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, @@ -5545,7 +5569,7 @@ bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type) } } } - + /* Static function to display route. */ static void route_vty_out_route (struct prefix *p, struct vty *vty) @@ -5608,6 +5632,8 @@ route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) vty_out (vty, "d"); else if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED)) vty_out (vty, ">"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_MULTIPATH)) + vty_out (vty, "="); else vty_out (vty, " "); @@ -6091,9 +6117,11 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, #endif /* HAVE_CLOCK_MONOTONIC */ } vty_out (vty, "%s", VTY_NEWLINE); -} - -#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s r RIB-failure, S Stale, R Removed%s" +} + +#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, "\ + "h history, * valid, > best, = multipath,%s"\ + " i internal, r RIB-failure, S Stale, R Removed%s" #define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path%s" #define BGP_SHOW_DAMP_HEADER " Network From Reuse Path%s" @@ -6777,7 +6805,7 @@ DEFUN (show_ip_bgp_view, IP_STR BGP_STR "BGP view\n" - "BGP view name\n") + "View name\n") { struct bgp *bgp; @@ -6799,7 +6827,7 @@ DEFUN (show_ip_bgp_view_route, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Network in the BGP routing table to display\n") { return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 0); @@ -6812,7 +6840,7 @@ DEFUN (show_ip_bgp_view_prefix, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "IP prefix /, e.g., 35.0.0.0/8\n") { return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 1); @@ -7066,7 +7094,7 @@ DEFUN (show_ipv6_mbgp_prefix, return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1); } #endif - + static int bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi, @@ -7210,7 +7238,7 @@ DEFUN (show_ipv6_mbgp_regexp, bgp_show_type_regexp); } #endif /* HAVE_IPV6 */ - + static int bgp_show_prefix_list (struct vty *vty, const char *prefix_list_str, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7325,7 +7353,7 @@ DEFUN (show_ipv6_mbgp_prefix_list, bgp_show_type_prefix_list); } #endif /* HAVE_IPV6 */ - + static int bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7439,7 +7467,7 @@ DEFUN (show_ipv6_mbgp_filter_list, bgp_show_type_filter_list); } #endif /* HAVE_IPV6 */ - + static int bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7524,7 +7552,7 @@ ALIAS (show_bgp_route_map, "Address family\n" "Display routes matching the route-map\n" "A route-map to match on\n") - + DEFUN (show_ip_bgp_cidr_only, show_ip_bgp_cidr_only_cmd, "show ip bgp cidr-only", @@ -7568,7 +7596,7 @@ DEFUN (show_ip_bgp_ipv4_cidr_only, return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_cidr_only, NULL); } - + DEFUN (show_ip_bgp_community_all, show_ip_bgp_community_all_cmd, "show ip bgp community", @@ -7646,7 +7674,7 @@ DEFUN (show_ipv6_mbgp_community_all, bgp_show_type_community_all, NULL); } #endif /* HAVE_IPV6 */ - + static int bgp_show_community (struct vty *vty, const char *view_name, int argc, const char **argv, int exact, afi_t afi, safi_t safi) @@ -7885,14 +7913,14 @@ DEFUN (show_bgp_view_afi_safi_community_all, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" #endif "Address Family modifier\n" "Address Family modifier\n" - "Display routes containing communities\n") + "Display routes matching the communities\n") { int afi; int safi; @@ -7926,7 +7954,7 @@ DEFUN (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -7963,7 +7991,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -7990,7 +8018,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -8021,7 +8049,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -8838,7 +8866,7 @@ ALIAS (show_ipv6_mbgp_community_exact, "Do not export to next AS (well-known community)\n" "Exact match of the communities") #endif /* HAVE_IPV6 */ - + static int bgp_show_community_list (struct vty *vty, const char *com, int exact, afi_t afi, safi_t safi) @@ -9025,7 +9053,7 @@ DEFUN (show_ipv6_mbgp_community_list_exact, return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST); } #endif /* HAVE_IPV6 */ - + static int bgp_show_prefix_longer (struct vty *vty, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -9217,7 +9245,7 @@ peer_lookup_in_view (struct vty *vty, const char *view_name, return peer; } - + enum bgp_stats { BGP_STATS_MAXBITLEN = 0, @@ -9565,7 +9593,7 @@ ALIAS (show_bgp_statistics_view, "Address family\n" "Address Family modifier\n" "BGP RIB advertisement statistics\n") - + enum bgp_pcounts { PCOUNT_ADJ_IN = 0, @@ -10105,7 +10133,7 @@ DEFUN (ipv6_mbgp_neighbor_advertised_route, return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 0); } #endif /* HAVE_IPV6 */ - + DEFUN (show_ip_bgp_view_neighbor_received_routes, show_ip_bgp_view_neighbor_received_routes_cmd, "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes", @@ -10179,7 +10207,7 @@ DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -10482,7 +10510,7 @@ ALIAS (show_bgp_view_neighbor_received_prefix_filter, "Display information received from a BGP neighbor\n" "Display the prefixlist filter\n") #endif /* HAVE_IPV6 */ - + static int bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -10594,7 +10622,7 @@ DEFUN (show_ip_bgp_view_rsclient, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR) { @@ -10644,7 +10672,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -10704,7 +10732,7 @@ DEFUN (show_ip_bgp_view_rsclient_route, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") @@ -10776,7 +10804,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -10858,7 +10886,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IP prefix /, e.g., 35.0.0.0/8\n") @@ -10930,7 +10958,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11012,7 +11040,7 @@ DEFUN (show_bgp_view_neighbor_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11038,7 +11066,7 @@ ALIAS (show_bgp_view_neighbor_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11051,7 +11079,7 @@ DEFUN (show_bgp_view_neighbor_damp, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11077,7 +11105,7 @@ ALIAS (show_bgp_view_neighbor_damp, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11090,7 +11118,7 @@ DEFUN (show_bgp_view_neighbor_flap, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11116,7 +11144,7 @@ ALIAS (show_bgp_view_neighbor_flap, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11227,7 +11255,7 @@ DEFUN (show_bgp_view_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR) { @@ -11276,7 +11304,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11335,7 +11363,7 @@ DEFUN (show_bgp_view_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") @@ -11406,7 +11434,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11487,7 +11515,7 @@ DEFUN (show_bgp_view_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IPv6 prefix /, e.g., 3ffe::/16\n") @@ -11558,7 +11586,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11634,7 +11662,7 @@ ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix, "IP prefix /, e.g., 3ffe::/16\n") #endif /* HAVE_IPV6 */ - + struct bgp_table *bgp_distance_table; struct bgp_distance @@ -11732,7 +11760,13 @@ bgp_distance_unset (struct vty *vty, const char *distance_str, } bdistance = rn->info; - + + if (bdistance->distance != distance) + { + vty_out (vty, "Distance does not match configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (bdistance->access_list) free (bdistance->access_list); bgp_distance_free (bdistance); @@ -11911,7 +11945,7 @@ DEFUN (no_bgp_distance_source_access_list, bgp_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + DEFUN (bgp_damp_set, bgp_damp_set_cmd, "bgp dampening <1-45> <1-20000> <1-20000> <1-255>", @@ -12006,7 +12040,7 @@ DEFUN (show_ip_bgp_flap_statistics, return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_flap_statistics, NULL); } - + /* Display specified route of BGP table. */ static int bgp_clear_damp_route (struct vty *vty, const char *view_name, @@ -12170,7 +12204,7 @@ DEFUN (clear_ip_bgp_dampening_address_mask, return bgp_clear_damp_route (vty, NULL, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 0); } - + static int bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int *write) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index f2204003..857781fe 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -93,14 +93,34 @@ o Cisco route-map tag : (This will not be implemented by bgpd) weight : Done -o Local extention +o Local extensions set ipv6 next-hop global: Done set ipv6 next-hop local : Done set as-path exclude : Done */ - + + /* generic as path object to be shared in multiple rules */ + +static void * +route_aspath_compile (const char *arg) +{ + struct aspath *aspath; + + aspath = aspath_str2aspath (arg); + if (! aspath) + return NULL; + return aspath; +} + +static void +route_aspath_free (void *rule) +{ + struct aspath *aspath = rule; + aspath_free (aspath); +} + /* 'match peer (A.B.C.D|X:X::X:X)' */ /* Compares the peer specified in the 'match peer' clause with the peer @@ -240,7 +260,7 @@ struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ @@ -292,7 +312,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip route-source ACCESS-LIST' */ /* Match function return 1 if match is success else return zero. */ @@ -350,7 +370,7 @@ struct route_map_rule_cmd route_match_ip_route_source_cmd = route_match_ip_route_source_compile, route_match_ip_route_source_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -390,7 +410,7 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = route_match_ip_address_prefix_list_compile, route_match_ip_address_prefix_list_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -437,7 +457,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip route-source prefix-list PREFIX_LIST' */ static route_map_result_t @@ -490,7 +510,7 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = route_match_ip_route_source_prefix_list_compile, route_match_ip_route_source_prefix_list_free }; - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ @@ -555,7 +575,7 @@ struct route_map_rule_cmd route_match_metric_cmd = route_match_metric_compile, route_match_metric_free }; - + /* `match as-path ASPATH' */ /* Match function for as-path match. I assume given object is */ @@ -603,7 +623,7 @@ struct route_map_rule_cmd route_match_aspath_cmd = route_match_aspath_compile, route_match_aspath_free }; - + /* `match community COMMUNIY' */ struct rmap_community { @@ -687,7 +707,7 @@ struct route_map_rule_cmd route_match_community_cmd = route_match_community_compile, route_match_community_free }; - + /* Match function for extcommunity match. */ static route_map_result_t route_match_ecommunity (void *rule, struct prefix *prefix, @@ -736,10 +756,10 @@ struct route_map_rule_cmd route_match_ecommunity_cmd = route_match_ecommunity_compile, route_match_ecommunity_free }; - + /* `match nlri` and `set nlri` are replaced by `address-family ipv4` and `address-family vpnv4'. */ - + /* `match origin' */ static route_map_result_t route_match_origin (void *rule, struct prefix *prefix, @@ -964,7 +984,7 @@ struct route_map_rule_cmd route_set_ip_nexthop_cmd = route_set_ip_nexthop_compile, route_set_ip_nexthop_free }; - + /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ @@ -1031,7 +1051,7 @@ struct route_map_rule_cmd route_set_local_pref_cmd = route_set_local_pref_compile, route_set_local_pref_free, }; - + /* `set weight WEIGHT' */ /* Set weight. */ @@ -1100,7 +1120,7 @@ struct route_map_rule_cmd route_set_weight_cmd = route_set_weight_compile, route_set_weight_free, }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -1154,7 +1174,6 @@ route_set_metric (void *rule, struct prefix *prefix, static void * route_set_metric_compile (const char *arg) { - u_int32_t metric; unsigned long larg; char *endptr = NULL; @@ -1165,7 +1184,6 @@ route_set_metric_compile (const char *arg) larg = strtoul (arg, &endptr, 10); if (*endptr != '\0' || errno || larg > UINT32_MAX) return NULL; - metric = larg; } else { @@ -1179,7 +1197,6 @@ route_set_metric_compile (const char *arg) larg = strtoul (arg+1, &endptr, 10); if (*endptr != '\0' || errno || larg > UINT32_MAX) return NULL; - metric = larg; } return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); @@ -1200,7 +1217,7 @@ struct route_map_rule_cmd route_set_metric_cmd = route_set_metric_compile, route_set_metric_free, }; - + /* `set as-path prepend ASPATH' */ /* For AS path prepend mechanism. */ @@ -1213,7 +1230,6 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t if (type == RMAP_BGP) { - aspath = rule; binfo = object; if (binfo->attr->aspath->refcnt) @@ -1221,34 +1237,44 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t else new = binfo->attr->aspath; - aspath_prepend (aspath, new); + if ((uintptr_t)rule > 10) + { + aspath = rule; + aspath_prepend (aspath, new); + } + else + { + as_t as = aspath_leftmost(new); + if (!as) as = binfo->peer->as; + new = aspath_add_seq_n (new, as, (uintptr_t) rule); + } + binfo->attr->aspath = new; } return RMAP_OKAY; } -/* Compile function for as-path prepend. */ static void * route_set_aspath_prepend_compile (const char *arg) { - struct aspath *aspath; + unsigned int num; - aspath = aspath_str2aspath (arg); - if (! aspath) - return NULL; - return aspath; + if (sscanf(arg, "last-as %u", &num) == 1 && num > 0 && num < 10) + return (void*)(uintptr_t)num; + + return route_aspath_compile(arg); } -/* Compile function for as-path prepend. */ static void route_set_aspath_prepend_free (void *rule) { - struct aspath *aspath = rule; - aspath_free (aspath); + if ((uintptr_t)rule > 10) + route_aspath_free(rule); } -/* Set metric rule structure. */ + +/* Set as-path prepend rule structure. */ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { "as-path prepend", @@ -1256,7 +1282,7 @@ struct route_map_rule_cmd route_set_aspath_prepend_cmd = route_set_aspath_prepend_compile, route_set_aspath_prepend_free, }; - + /* `set as-path exclude ASn' */ /* For ASN exclude mechanism. @@ -1282,39 +1308,15 @@ route_set_aspath_exclude (void *rule, struct prefix *dummy, route_map_object_t t return RMAP_OKAY; } -/* FIXME: consider using route_set_aspath_prepend_compile() and - * route_set_aspath_prepend_free(), which two below function are - * exact clones of. - */ - -/* Compile function for as-path exclude. */ -static void * -route_set_aspath_exclude_compile (const char *arg) -{ - struct aspath *aspath; - - aspath = aspath_str2aspath (arg); - if (! aspath) - return NULL; - return aspath; -} - -static void -route_set_aspath_exclude_free (void *rule) -{ - struct aspath *aspath = rule; - aspath_free (aspath); -} - /* Set ASn exlude rule structure. */ struct route_map_rule_cmd route_set_aspath_exclude_cmd = { "as-path exclude", route_set_aspath_exclude, - route_set_aspath_exclude_compile, - route_set_aspath_exclude_free, + route_aspath_compile, + route_aspath_free, }; - + /* `set community COMMUNITY' */ struct rmap_com_set { @@ -1347,6 +1349,9 @@ route_set_community (void *rule, struct prefix *prefix, { attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)); attr->community = NULL; + /* See the longer comment down below. */ + if (old && old->refcnt == 0) + community_free(old); return RMAP_OKAY; } @@ -1435,7 +1440,7 @@ struct route_map_rule_cmd route_set_community_cmd = route_set_community_compile, route_set_community_free, }; - + /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ @@ -1524,13 +1529,13 @@ struct route_map_rule_cmd route_set_community_delete_cmd = route_set_community_delete_compile, route_set_community_delete_free, }; - + /* `set extcommunity rt COMMUNITY' */ -/* For community set mechanism. */ +/* For community set mechanism. Used by _rt and _soo. */ static route_map_result_t -route_set_ecommunity_rt (void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +route_set_ecommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { struct ecommunity *ecom; struct ecommunity *new_ecom; @@ -1549,14 +1554,19 @@ route_set_ecommunity_rt (void *rule, struct prefix *prefix, old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; if (old_ecom) - new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); + { + new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); + + /* old_ecom->refcnt = 1 => owned elsewhere, e.g. bgp_update_receive() + * ->refcnt = 0 => set by a previous route-map statement */ + if (!old_ecom->refcnt) + ecommunity_free (&old_ecom); + } else new_ecom = ecommunity_dup (ecom); - bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); - - if (old_ecom) - ecommunity_unintern (&old_ecom); + /* will be intern()'d or attr_flush()'d by bgp_update_main() */ + bgp_info->attr->extra->ecommunity = new_ecom; bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); } @@ -1575,9 +1585,9 @@ route_set_ecommunity_rt_compile (const char *arg) return ecommunity_intern (ecom); } -/* Free function for set community. */ +/* Free function for set community. Used by _rt and _soo */ static void -route_set_ecommunity_rt_free (void *rule) +route_set_ecommunity_free (void *rule) { struct ecommunity *ecom = rule; ecommunity_unintern (&ecom); @@ -1587,46 +1597,13 @@ route_set_ecommunity_rt_free (void *rule) struct route_map_rule_cmd route_set_ecommunity_rt_cmd = { "extcommunity rt", - route_set_ecommunity_rt, + route_set_ecommunity, route_set_ecommunity_rt_compile, - route_set_ecommunity_rt_free, + route_set_ecommunity_free, }; /* `set extcommunity soo COMMUNITY' */ -/* For community set mechanism. */ -static route_map_result_t -route_set_ecommunity_soo (void *rule, struct prefix *prefix, - route_map_object_t type, void *object) -{ - struct ecommunity *ecom, *old_ecom, *new_ecom; - struct bgp_info *bgp_info; - - if (type == RMAP_BGP) - { - ecom = rule; - bgp_info = object; - - if (! ecom) - return RMAP_OKAY; - - old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; - - if (old_ecom) - new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); - else - new_ecom = ecommunity_dup (ecom); - - bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); - - if (old_ecom) - ecommunity_unintern (&old_ecom); - - bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); - } - return RMAP_OKAY; -} - /* Compile function for set community. */ static void * route_set_ecommunity_soo_compile (const char *arg) @@ -1640,23 +1617,15 @@ route_set_ecommunity_soo_compile (const char *arg) return ecommunity_intern (ecom); } -/* Free function for set community. */ -static void -route_set_ecommunity_soo_free (void *rule) -{ - struct ecommunity *ecom = rule; - ecommunity_unintern (&ecom); -} - /* Set community rule structure. */ struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { "extcommunity soo", - route_set_ecommunity_soo, + route_set_ecommunity, route_set_ecommunity_soo_compile, - route_set_ecommunity_soo_free, + route_set_ecommunity_free, }; - + /* `set origin ORIGIN' */ /* For origin set. */ @@ -1702,7 +1671,7 @@ route_set_origin_free (void *rule) XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); } -/* Set metric rule structure. */ +/* Set origin rule structure. */ struct route_map_rule_cmd route_set_origin_cmd = { "origin", @@ -1710,7 +1679,7 @@ struct route_map_rule_cmd route_set_origin_cmd = route_set_origin_compile, route_set_origin_free, }; - + /* `set atomic-aggregate' */ /* For atomic aggregate set. */ @@ -1751,7 +1720,7 @@ struct route_map_rule_cmd route_set_atomic_aggregate_cmd = route_set_atomic_aggregate_compile, route_set_atomic_aggregate_free, }; - + /* `set aggregator as AS A.B.C.D' */ struct aggregator { @@ -1810,7 +1779,7 @@ struct route_map_rule_cmd route_set_aggregator_as_cmd = route_set_aggregator_as_compile, route_set_aggregator_as_free, }; - + #ifdef HAVE_IPV6 /* `match ipv6 address IP_ACCESS_LIST' */ @@ -1852,29 +1821,28 @@ struct route_map_rule_cmd route_match_ipv6_address_cmd = route_match_ipv6_address_compile, route_match_ipv6_address_free }; - + /* `match ipv6 next-hop IP_ADDRESS' */ static route_map_result_t route_match_ipv6_next_hop (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - struct in6_addr *addr; + struct in6_addr *addr = rule; struct bgp_info *bgp_info; if (type == RMAP_BGP) { - addr = rule; bgp_info = object; if (!bgp_info->attr->extra) return RMAP_NOMATCH; - if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, rule)) + if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, addr)) return RMAP_MATCH; if (bgp_info->attr->extra->mp_nexthop_len == 32 && - IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, rule)) + IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, addr)) return RMAP_MATCH; return RMAP_NOMATCH; @@ -1914,7 +1882,7 @@ struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = route_match_ipv6_next_hop_compile, route_match_ipv6_next_hop_free }; - + /* `match ipv6 address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -1954,7 +1922,7 @@ struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = route_match_ipv6_address_prefix_list_compile, route_match_ipv6_address_prefix_list_free }; - + /* `set ipv6 nexthop global IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ @@ -2018,7 +1986,7 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd = route_set_ipv6_nexthop_global_compile, route_set_ipv6_nexthop_global_free }; - + /* `set ipv6 nexthop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ @@ -2082,8 +2050,99 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = route_set_ipv6_nexthop_local_compile, route_set_ipv6_nexthop_local_free }; + +/* `set ipv6 nexthop peer-address' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in6_addr peer_address; + struct bgp_info *bgp_info; + struct peer *peer; + char peer_addr_buf[INET6_ADDRSTRLEN]; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + bgp_info = object; + peer = bgp_info->peer; + + if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) || + CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) + && peer->su_remote + && sockunion_family (peer->su_remote) == AF_INET6) + { + inet_pton (AF_INET6, sockunion2str (peer->su_remote, + peer_addr_buf, + INET6_ADDRSTRLEN), + &peer_address); + } + else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT) + && peer->su_local + && sockunion_family (peer->su_local) == AF_INET6) + { + inet_pton (AF_INET, sockunion2str (peer->su_local, + peer_addr_buf, + INET6_ADDRSTRLEN), + &peer_address); + } + + if (IN6_IS_ADDR_LINKLOCAL(&peer_address)) + { + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = peer_address; + + /* Set nexthop length. */ + if (bgp_info->attr->extra->mp_nexthop_len != 32) + bgp_info->attr->extra->mp_nexthop_len = 32; + } + else + { + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = peer_address; + + /* Set nexthop length. */ + if (bgp_info->attr->extra->mp_nexthop_len == 0) + bgp_info->attr->extra->mp_nexthop_len = 16; + } + } + + return RMAP_OKAY; +} + +/* Route map `ip next-hop' compile function. Given string is converted + to struct in_addr structure. */ +static void * +route_set_ipv6_nexthop_peer_compile (const char *arg) +{ + int *rins = NULL; + + rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (int)); + *rins = 1; + + return rins; +} + +/* Free route map's compiled `ip next-hop' value. */ +static void +route_set_ipv6_nexthop_peer_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip nexthop set. */ +struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = +{ + "ipv6 next-hop peer-address", + route_set_ipv6_nexthop_peer, + route_set_ipv6_nexthop_peer_compile, + route_set_ipv6_nexthop_peer_free +}; + #endif /* HAVE_IPV6 */ - + /* `set vpnv4 nexthop A.B.C.D' */ static route_map_result_t @@ -2139,7 +2198,7 @@ struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = route_set_vpnv4_nexthop_compile, route_set_vpnv4_nexthop_free }; - + /* `set originator-id' */ /* For origin set. */ @@ -2188,7 +2247,7 @@ route_set_originator_id_free (void *rule) XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); } -/* Set metric rule structure. */ +/* Set originator-id rule structure. */ struct route_map_rule_cmd route_set_originator_id_cmd = { "originator-id", @@ -2196,7 +2255,7 @@ struct route_map_rule_cmd route_set_originator_id_cmd = route_set_originator_id_compile, route_set_originator_id_free, }; - + /* Add bgp route map rule. */ static int bgp_route_match_add (struct vty *vty, struct route_map_index *index, @@ -2405,7 +2464,7 @@ bgp_route_map_update (const char *unused) } } } - + DEFUN (match_peer, match_peer_cmd, "match peer (A.B.C.D|X:X::X:X)", @@ -3097,6 +3156,15 @@ DEFUN (set_aspath_prepend, return ret; } +ALIAS (set_aspath_prepend, + set_aspath_prepend_lastas_cmd, + "set as-path prepend (last-as) <1-10>", + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "Use the peer's AS-number\n" + "Number of times to insert"); + DEFUN (no_set_aspath_prepend, no_set_aspath_prepend_cmd, "no set as-path prepend", @@ -3487,7 +3555,7 @@ DEFUN (set_aggregator_as, "IP address of aggregator\n") { int ret; - as_t as; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ struct in_addr address; char *argstr; @@ -3521,7 +3589,7 @@ DEFUN (no_set_aggregator_as, "AS number of aggregator\n") { int ret; - as_t as; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ struct in_addr address; char *argstr; @@ -3559,7 +3627,7 @@ ALIAS (no_set_aggregator_as, "AS number\n" "IP address of aggregator\n") - + #ifdef HAVE_IPV6 DEFUN (match_ipv6_address, match_ipv6_address_cmd, @@ -3632,6 +3700,29 @@ DEFUN (no_match_ipv6_address_prefix_list, return bgp_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]); } +DEFUN (set_ipv6_nexthop_peer, + set_ipv6_nexthop_peer_cmd, + "set ipv6 next-hop peer-address", + SET_STR + IPV6_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") +{ + return bgp_route_set_add (vty, vty->index, "ipv6 next-hop peer-address", NULL); +} + +DEFUN (no_set_ipv6_nexthop_peer, + no_set_ipv6_nexthop_peer_cmd, + "no set ipv6 next-hop peer-address", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + ) +{ + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop", argv[0]); +} + DEFUN (set_ipv6_nexthop_global, set_ipv6_nexthop_global_cmd, "set ipv6 next-hop global X:X::X:X", @@ -3830,7 +3921,7 @@ ALIAS (no_match_pathlimit_as, "BGP AS-Pathlimit attribute\n" "Match Pathlimit ASN\n") - + /* Initialization of route map. */ void bgp_route_map_init (void) @@ -3930,6 +4021,7 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &no_set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_val_cmd); install_element (RMAP_NODE, &set_aspath_prepend_cmd); + install_element (RMAP_NODE, &set_aspath_prepend_lastas_cmd); install_element (RMAP_NODE, &set_aspath_exclude_cmd); install_element (RMAP_NODE, &no_set_aspath_prepend_cmd); install_element (RMAP_NODE, &no_set_aspath_prepend_val_cmd); @@ -3970,7 +4062,8 @@ bgp_route_map_init (void) route_map_install_match (&route_match_ipv6_address_prefix_list_cmd); route_map_install_set (&route_set_ipv6_nexthop_global_cmd); route_map_install_set (&route_set_ipv6_nexthop_local_cmd); - + route_map_install_set (&route_set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &match_ipv6_address_cmd); install_element (RMAP_NODE, &no_match_ipv6_address_cmd); install_element (RMAP_NODE, &match_ipv6_next_hop_cmd); @@ -3983,6 +4076,8 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd); install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); #endif /* HAVE_IPV6 */ /* AS-Pathlimit: functionality removed, commands kept for diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index c8f2aa54..79aaa03a 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -38,7 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_snmp.h" - + /* BGP4-MIB described in RFC1657. */ #define BGP4MIB 1,3,6,1,2,1,15 @@ -112,7 +112,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define OCTET_STRING ASN_OCTET_STR #define IPADDRESS ASN_IPADDRESS #define GAUGE32 ASN_UNSIGNED - + /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES @@ -242,7 +242,7 @@ struct variable bgp_variables[] = 3, {6, 1, 14}}, }; - + static u_char * bgpVersion (struct variable *v, oid name[], size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -835,7 +835,7 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length, } return NULL; } - + /* BGP Traps. */ struct trap_object bgpTrapList[] = { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index cb7ff1fa..e6a36605 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -314,7 +314,7 @@ DEFUN_DEPRECATED (neighbor_version, { return CMD_SUCCESS; } - + /* "router bgp" commands. */ DEFUN (router_bgp, router_bgp_cmd, @@ -364,7 +364,7 @@ ALIAS (router_bgp, AS_STR "BGP view\n" "view name\n") - + /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, @@ -405,7 +405,7 @@ ALIAS (no_router_bgp, AS_STR "BGP view\n" "view name\n") - + /* BGP router-id. */ DEFUN (bgp_router_id, @@ -476,7 +476,7 @@ ALIAS (no_bgp_router_id, BGP_STR "Override configured router identifier\n" "Manually configured router identifier\n") - + /* BGP Cluster ID. */ DEFUN (bgp_cluster_id, @@ -546,7 +546,7 @@ ALIAS (no_bgp_cluster_id, BGP_STR "Configure Route-Reflector Cluster-id\n" "Route-Reflector Cluster-id in IP address format\n") - + DEFUN (bgp_confederation_identifier, bgp_confederation_identifier_cmd, "bgp confederation identifier " CMD_AS_RANGE, @@ -576,7 +576,7 @@ DEFUN (no_bgp_confederation_identifier, "AS number\n") { struct bgp *bgp; - as_t as; + as_t as __attribute__((unused)); /* Dummy for VTY_GET_INTEGER_RANGE */ bgp = vty->index; @@ -596,7 +596,7 @@ ALIAS (no_bgp_confederation_identifier, "AS confederation parameters\n" "AS number\n" "Set routing domain confederation AS\n") - + DEFUN (bgp_confederation_peers, bgp_confederation_peers_cmd, "bgp confederation peers ." CMD_AS_RANGE, @@ -650,7 +650,7 @@ DEFUN (no_bgp_confederation_peers, } return CMD_SUCCESS; } - + /* Maximum-paths configuration */ DEFUN (bgp_maxpaths, bgp_maxpaths_cmd, @@ -793,7 +793,7 @@ bgp_config_write_maxpaths (struct vty *vty, struct bgp *bgp, afi_t afi, return 0; } - + /* BGP timers. */ DEFUN (bgp_timers, @@ -849,7 +849,7 @@ ALIAS (no_bgp_timers, "BGP timers\n" "Keepalive interval\n" "Holdtime\n") - + DEFUN (bgp_client_to_client_reflection, bgp_client_to_client_reflection_cmd, "bgp client-to-client reflection", @@ -906,7 +906,7 @@ DEFUN (no_bgp_always_compare_med, bgp_flag_unset (bgp, BGP_FLAG_ALWAYS_COMPARE_MED); return CMD_SUCCESS; } - + /* "bgp deterministic-med" configuration. */ DEFUN (bgp_deterministic_med, bgp_deterministic_med_cmd, @@ -1037,7 +1037,7 @@ DEFUN (no_bgp_fast_external_failover, bgp_flag_set (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER); return CMD_SUCCESS; } - + /* "bgp enforce-first-as" configuration. */ DEFUN (bgp_enforce_first_as, bgp_enforce_first_as_cmd, @@ -1065,7 +1065,7 @@ DEFUN (no_bgp_enforce_first_as, bgp_flag_unset (bgp, BGP_FLAG_ENFORCE_FIRST_AS); return CMD_SUCCESS; } - + /* "bgp bestpath compare-routerid" configuration. */ DEFUN (bgp_bestpath_compare_router_id, bgp_bestpath_compare_router_id_cmd, @@ -1095,7 +1095,7 @@ DEFUN (no_bgp_bestpath_compare_router_id, bgp_flag_unset (bgp, BGP_FLAG_COMPARE_ROUTER_ID); return CMD_SUCCESS; } - + /* "bgp bestpath as-path ignore" configuration. */ DEFUN (bgp_bestpath_aspath_ignore, bgp_bestpath_aspath_ignore_cmd, @@ -1127,7 +1127,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore, bgp_flag_unset (bgp, BGP_FLAG_ASPATH_IGNORE); return CMD_SUCCESS; } - + /* "bgp bestpath as-path confed" configuration. */ DEFUN (bgp_bestpath_aspath_confed, bgp_bestpath_aspath_confed_cmd, @@ -1159,7 +1159,39 @@ DEFUN (no_bgp_bestpath_aspath_confed, bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED); return CMD_SUCCESS; } - + +/* "bgp bestpath as-path multipath-relax" configuration. */ +DEFUN (bgp_bestpath_aspath_multipath_relax, + bgp_bestpath_aspath_multipath_relax_cmd, + "bgp bestpath as-path multipath-relax", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Allow load sharing across routes that have different AS paths (but same length)\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_aspath_multipath_relax, + no_bgp_bestpath_aspath_multipath_relax_cmd, + "no bgp bestpath as-path multipath-relax", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Allow load sharing across routes that have different AS paths (but same length)\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + return CMD_SUCCESS; +} + /* "bgp log-neighbor-changes" configuration. */ DEFUN (bgp_log_neighbor_changes, bgp_log_neighbor_changes_cmd, @@ -1187,7 +1219,7 @@ DEFUN (no_bgp_log_neighbor_changes, bgp_flag_unset (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); return CMD_SUCCESS; } - + /* "bgp bestpath med" configuration. */ DEFUN (bgp_bestpath_med, bgp_bestpath_med_cmd, @@ -1285,7 +1317,7 @@ ALIAS (no_bgp_bestpath_med2, "MED attribute\n" "Treat missing MED as the least preferred one\n" "Compare MED among confederation paths\n") - + /* "no bgp default ipv4-unicast". */ DEFUN (no_bgp_default_ipv4_unicast, no_bgp_default_ipv4_unicast_cmd, @@ -1315,7 +1347,7 @@ DEFUN (bgp_default_ipv4_unicast, bgp_flag_unset (bgp, BGP_FLAG_NO_DEFAULT_IPV4); return CMD_SUCCESS; } - + /* "bgp import-check" configuration. */ DEFUN (bgp_network_import_check, bgp_network_import_check_cmd, @@ -1345,7 +1377,7 @@ DEFUN (no_bgp_network_import_check, bgp_flag_unset (bgp, BGP_FLAG_IMPORT_CHECK); return CMD_SUCCESS; } - + DEFUN (bgp_default_local_preference, bgp_default_local_preference_cmd, "bgp default local-preference <0-4294967295>", @@ -1389,7 +1421,7 @@ ALIAS (no_bgp_default_local_preference, "Configure BGP defaults\n" "local preference (higher=more preferred)\n" "Configure default local preference value\n") - + static int peer_remote_as_vty (struct vty *vty, const char *peer_str, const char *as_str, afi_t afi, safi_t safi) @@ -1449,7 +1481,7 @@ DEFUN (neighbor_remote_as, { return peer_remote_as_vty (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST); } - + DEFUN (neighbor_peer_group, neighbor_peer_group_cmd, "neighbor WORD peer-group", @@ -1554,7 +1586,7 @@ DEFUN (no_neighbor_peer_group_remote_as, } return CMD_SUCCESS; } - + DEFUN (neighbor_local_as, neighbor_local_as_cmd, NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE, @@ -1570,7 +1602,7 @@ DEFUN (neighbor_local_as, if (! peer) return CMD_WARNING; - ret = peer_local_as_set (peer, atoi (argv[1]), 0); + ret = peer_local_as_set (peer, atoi (argv[1]), 0, 0); return bgp_vty_return (vty, ret); } @@ -1590,10 +1622,32 @@ DEFUN (neighbor_local_as_no_prepend, if (! peer) return CMD_WARNING; - ret = peer_local_as_set (peer, atoi (argv[1]), 1); + ret = peer_local_as_set (peer, atoi (argv[1]), 1, 0); + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_local_as_no_prepend_replace_as, + neighbor_local_as_no_prepend_replace_as_cmd, + NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\n" + "Do not prepend local-as to updates from ebgp peers\n" + "Do not prepend local-as to updates from ibgp peers\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_local_as_set (peer, atoi (argv[1]), 1, 1); return bgp_vty_return (vty, ret); } + DEFUN (no_neighbor_local_as, no_neighbor_local_as_cmd, NO_NEIGHBOR_CMD2 "local-as", @@ -1631,7 +1685,18 @@ ALIAS (no_neighbor_local_as, "Specify a local-as number\n" "AS number used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n") - + +ALIAS (no_neighbor_local_as, + no_neighbor_local_as_val3_cmd, + NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\n" + "Do not prepend local-as to updates from ebgp peers\n" + "Do not prepend local-as to updates from ibgp peers\n") + DEFUN (neighbor_password, neighbor_password_cmd, NEIGHBOR_CMD2 "password LINE", @@ -1669,7 +1734,7 @@ DEFUN (no_neighbor_password, ret = peer_password_unset (peer); return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_activate, neighbor_activate_cmd, NEIGHBOR_CMD2 "activate", @@ -1708,7 +1773,7 @@ DEFUN (no_neighbor_activate, return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_set_peer_group, neighbor_set_peer_group_cmd, NEIGHBOR_CMD "peer-group WORD", @@ -1790,7 +1855,7 @@ DEFUN (no_neighbor_set_peer_group, return bgp_vty_return (vty, ret); } - + static int peer_flag_modify_vty (struct vty *vty, const char *ip_str, u_int16_t flag, int set) @@ -1843,7 +1908,7 @@ DEFUN (no_neighbor_passive, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE); } - + /* neighbor shutdown. */ DEFUN (neighbor_shutdown, neighbor_shutdown_cmd, @@ -1865,7 +1930,7 @@ DEFUN (no_neighbor_shutdown, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_SHUTDOWN); } - + /* Deprecated neighbor capability route-refresh. */ DEFUN_DEPRECATED (neighbor_capability_route_refresh, neighbor_capability_route_refresh_cmd, @@ -1889,7 +1954,7 @@ DEFUN_DEPRECATED (no_neighbor_capability_route_refresh, { return CMD_SUCCESS; } - + /* neighbor capability dynamic. */ DEFUN (neighbor_capability_dynamic, neighbor_capability_dynamic_cmd, @@ -1913,7 +1978,7 @@ DEFUN (no_neighbor_capability_dynamic, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY); } - + /* neighbor dont-capability-negotiate */ DEFUN (neighbor_dont_capability_negotiate, neighbor_dont_capability_negotiate_cmd, @@ -1935,7 +2000,7 @@ DEFUN (no_neighbor_dont_capability_negotiate, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY); } - + static int peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, u_int32_t flag, int set) @@ -1968,7 +2033,7 @@ peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi, { return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 0); } - + /* neighbor capability orf prefix-list. */ DEFUN (neighbor_capability_orf_prefix, neighbor_capability_orf_prefix_cmd, @@ -2024,31 +2089,47 @@ DEFUN (no_neighbor_capability_orf_prefix, return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), flag); } - + /* neighbor next-hop-self. */ DEFUN (neighbor_nexthop_self, neighbor_nexthop_self_cmd, - NEIGHBOR_CMD2 "next-hop-self", + NEIGHBOR_CMD2 "next-hop-self {all}", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Disable the next hop calculation for this neighbor\n") + "Disable the next hop calculation for this neighbor\n" + "Apply also to ibgp-learned routes when acting as a route reflector\n") { - return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), - bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF); + u_int32_t flags = PEER_FLAG_NEXTHOP_SELF, unset = 0; + int rc; + + /* Check if "all" is specified */ + if (argv[1] != NULL) + flags |= PEER_FLAG_NEXTHOP_SELF_ALL; + else + unset |= PEER_FLAG_NEXTHOP_SELF_ALL; + + rc = peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); + if ( rc == CMD_SUCCESS && unset ) + rc = peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), unset); + return rc; } DEFUN (no_neighbor_nexthop_self, no_neighbor_nexthop_self_cmd, - NO_NEIGHBOR_CMD2 "next-hop-self", + NO_NEIGHBOR_CMD2 "next-hop-self {all}", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Disable the next hop calculation for this neighbor\n") + "Disable the next hop calculation for this neighbor\n" + "Apply also to ibgp-learned routes when acting as a route reflector\n") { return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), - bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF); + bgp_node_safi (vty), + PEER_FLAG_NEXTHOP_SELF|PEER_FLAG_NEXTHOP_SELF_ALL); } - + /* neighbor remove-private-AS. */ DEFUN (neighbor_remove_private_as, neighbor_remove_private_as_cmd, @@ -2074,7 +2155,7 @@ DEFUN (no_neighbor_remove_private_as, bgp_node_safi (vty), PEER_FLAG_REMOVE_PRIVATE_AS); } - + /* neighbor send-community. */ DEFUN (neighbor_send_community, neighbor_send_community_cmd, @@ -2100,7 +2181,7 @@ DEFUN (no_neighbor_send_community, bgp_node_safi (vty), PEER_FLAG_SEND_COMMUNITY); } - + /* neighbor send-community extended. */ DEFUN (neighbor_send_community_type, neighbor_send_community_type_cmd, @@ -2152,7 +2233,7 @@ DEFUN (no_neighbor_send_community_type, (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY)); } - + /* neighbor soft-reconfig. */ DEFUN (neighbor_soft_reconfiguration, neighbor_soft_reconfiguration_cmd, @@ -2180,7 +2261,7 @@ DEFUN (no_neighbor_soft_reconfiguration, bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_SOFT_RECONFIG); } - + DEFUN (neighbor_route_reflector_client, neighbor_route_reflector_client_cmd, NEIGHBOR_CMD2 "route-reflector-client", @@ -2212,7 +2293,7 @@ DEFUN (no_neighbor_route_reflector_client, bgp_node_safi (vty), PEER_FLAG_REFLECTOR_CLIENT); } - + static int peer_rsclient_set_vty (struct vty *vty, const char *peer_str, int afi, int safi) @@ -2360,7 +2441,7 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, return CMD_SUCCESS; } - + /* neighbor route-server-client. */ DEFUN (neighbor_route_server_client, neighbor_route_server_client_cmd, @@ -2384,7 +2465,7 @@ DEFUN (no_neighbor_route_server_client, return peer_rsclient_unset_vty (vty, argv[0], bgp_node_afi(vty), bgp_node_safi(vty)); } - + DEFUN (neighbor_nexthop_local_unchanged, neighbor_nexthop_local_unchanged_cmd, NEIGHBOR_CMD2 "nexthop-local unchanged", @@ -2397,7 +2478,7 @@ DEFUN (neighbor_nexthop_local_unchanged, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED ); } - + DEFUN (no_neighbor_nexthop_local_unchanged, no_neighbor_nexthop_local_unchanged_cmd, NO_NEIGHBOR_CMD2 "nexthop-local unchanged", @@ -2411,7 +2492,7 @@ DEFUN (no_neighbor_nexthop_local_unchanged, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED ); } - + DEFUN (neighbor_attr_unchanged, neighbor_attr_unchanged_cmd, NEIGHBOR_CMD2 "attribute-unchanged", @@ -2768,7 +2849,7 @@ DEFUN_DEPRECATED (neighbor_transparent_nexthop, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_UNCHANGED); } - + /* EBGP multihop configuration. */ static int peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str, @@ -2842,7 +2923,7 @@ ALIAS (no_neighbor_ebgp_multihop, NEIGHBOR_ADDR_STR2 "Allow EBGP neighbors not on directly connected networks\n" "maximum hop count\n") - + /* disable-connected-check */ DEFUN (neighbor_disable_connected_check, neighbor_disable_connected_check_cmd, @@ -2881,7 +2962,7 @@ ALIAS (no_neighbor_disable_connected_check, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Enforce EBGP neighbors perform multihop\n") - + DEFUN (neighbor_description, neighbor_description_cmd, NEIGHBOR_CMD2 "description .LINE", @@ -2936,7 +3017,7 @@ ALIAS (no_neighbor_description, NEIGHBOR_ADDR_STR2 "Neighbor specific description\n" "Up to 80 characters describing this neighbor\n") - + /* Neighbor update-source. */ static int peer_update_source_vty (struct vty *vty, const char *peer_str, @@ -2991,7 +3072,7 @@ DEFUN (no_neighbor_update_source, { return peer_update_source_vty (vty, argv[0], NULL); } - + static int peer_default_originate_set_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, @@ -3058,7 +3139,7 @@ ALIAS (no_neighbor_default_originate, "Originate default route to this neighbor\n" "Route-map to specify criteria to originate default\n" "route-map name\n") - + /* Set neighbor's BGP port. */ static int peer_port_vty (struct vty *vty, const char *ip_str, int afi, @@ -3118,13 +3199,12 @@ ALIAS (no_neighbor_port, NEIGHBOR_ADDR_STR "Neighbor's BGP port\n" "TCP port number\n") - + /* neighbor weight. */ static int peer_weight_set_vty (struct vty *vty, const char *ip_str, const char *weight_str) { - int ret; struct peer *peer; unsigned long weight; @@ -3134,9 +3214,7 @@ peer_weight_set_vty (struct vty *vty, const char *ip_str, VTY_GET_INTEGER_RANGE("weight", weight, weight_str, 0, 65535); - ret = peer_weight_set (peer, weight); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_weight_set (peer, weight)); } static int @@ -3148,9 +3226,7 @@ peer_weight_unset_vty (struct vty *vty, const char *ip_str) if (! peer) return CMD_WARNING; - peer_weight_unset (peer); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_weight_unset (peer)); } DEFUN (neighbor_weight, @@ -3183,7 +3259,7 @@ ALIAS (no_neighbor_weight, NEIGHBOR_ADDR_STR2 "Set default weight for routes from this neighbor\n" "default weight\n") - + /* Override capability negotiation. */ DEFUN (neighbor_override_capability, neighbor_override_capability_cmd, @@ -3205,7 +3281,7 @@ DEFUN (no_neighbor_override_capability, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY); } - + DEFUN (neighbor_strict_capability, neighbor_strict_capability_cmd, NEIGHBOR_CMD "strict-capability-match", @@ -3226,7 +3302,7 @@ DEFUN (no_neighbor_strict_capability, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH); } - + static int peer_timers_set_vty (struct vty *vty, const char *ip_str, const char *keep_str, const char *hold_str) @@ -3247,7 +3323,7 @@ peer_timers_set_vty (struct vty *vty, const char *ip_str, return bgp_vty_return (vty, ret); } - + static int peer_timers_unset_vty (struct vty *vty, const char *ip_str) { @@ -3285,12 +3361,11 @@ DEFUN (no_neighbor_timers, { return peer_timers_unset_vty (vty, argv[0]); } - + static int peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, const char *time_str) { - int ret; struct peer *peer; u_int32_t connect; @@ -3300,24 +3375,19 @@ peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, VTY_GET_INTEGER_RANGE ("Connect time", connect, time_str, 0, 65535); - ret = peer_timers_connect_set (peer, connect); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_timers_connect_set (peer, connect)); } static int peer_timers_connect_unset_vty (struct vty *vty, const char *ip_str) { - int ret; struct peer *peer; peer = peer_and_group_lookup_vty (vty, ip_str); if (! peer) return CMD_WARNING; - ret = peer_timers_connect_unset (peer); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_timers_connect_unset (peer)); } DEFUN (neighbor_timers_connect, @@ -3353,7 +3423,7 @@ ALIAS (no_neighbor_timers_connect, "BGP per neighbor timers\n" "BGP connect timer\n" "Connect timer\n") - + static int peer_advertise_interval_vty (struct vty *vty, const char *ip_str, const char *time_str, int set) @@ -3374,7 +3444,7 @@ peer_advertise_interval_vty (struct vty *vty, const char *ip_str, else ret = peer_advertise_interval_unset (peer); - return CMD_SUCCESS; + return bgp_vty_return (vty, ret); } DEFUN (neighbor_advertise_interval, @@ -3407,7 +3477,7 @@ ALIAS (no_neighbor_advertise_interval, NEIGHBOR_ADDR_STR "Minimum interval between sending BGP routing updates\n" "time in seconds\n") - + /* neighbor interface */ static int peer_interface_vty (struct vty *vty, const char *ip_str, const char *str) @@ -3424,7 +3494,7 @@ peer_interface_vty (struct vty *vty, const char *ip_str, const char *str) else ret = peer_interface_unset (peer); - return CMD_SUCCESS; + return bgp_vty_return (vty, ret); } DEFUN (neighbor_interface, @@ -3449,7 +3519,7 @@ DEFUN (no_neighbor_interface, { return peer_interface_vty (vty, argv[0], NULL); } - + /* Set distribute list to the peer. */ static int peer_distribute_set_vty (struct vty *vty, const char *ip_str, @@ -3530,7 +3600,7 @@ DEFUN (no_neighbor_distribute_list, return peer_distribute_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set prefix list to the peer. */ static int peer_prefix_list_set_vty (struct vty *vty, const char *ip_str, afi_t afi, @@ -3607,7 +3677,7 @@ DEFUN (no_neighbor_prefix_list, return peer_prefix_list_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + static int peer_aslist_set_vty (struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, @@ -3684,7 +3754,7 @@ DEFUN (no_neighbor_filter_list, return peer_aslist_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set route-map to the peer. */ static int peer_route_map_set_vty (struct vty *vty, const char *ip_str, @@ -3773,7 +3843,7 @@ DEFUN (no_neighbor_route_map, return peer_route_map_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set unsuppress-map to the peer. */ static int peer_unsuppress_map_set_vty (struct vty *vty, const char *ip_str, afi_t afi, @@ -3832,7 +3902,7 @@ DEFUN (no_neighbor_unsuppress_map, return peer_unsuppress_map_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty)); } - + static int peer_maximum_prefix_set_vty (struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, const char *num_str, @@ -4042,7 +4112,7 @@ ALIAS (no_neighbor_maximum_prefix, "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes") - + /* "neighbor allowas-in" */ DEFUN (neighbor_allowas_in, neighbor_allowas_in_cmd, @@ -4097,7 +4167,7 @@ DEFUN (no_neighbor_allowas_in, return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_ttl_security, neighbor_ttl_security_cmd, NEIGHBOR_CMD2 "ttl-security hops <1-254>", @@ -4133,7 +4203,7 @@ DEFUN (no_neighbor_ttl_security, return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer)); } - + /* Address family configuration. */ DEFUN (address_family_ipv4, address_family_ipv4_cmd, @@ -4217,7 +4287,7 @@ DEFUN (exit_address_family, vty->node = BGP_NODE; return CMD_SUCCESS; } - + /* BGP clear sort. */ enum clear_sort { @@ -4571,7 +4641,7 @@ ALIAS (clear_ip_bgp_as, BGP_STR "Address family\n" "Clear peers with the AS number\n") - + /* Outbound soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft_out, clear_ip_bgp_all_soft_out_cmd, @@ -5198,7 +5268,7 @@ ALIAS (clear_bgp_as_soft_out, "Address family\n" "Clear peers with the AS number\n" "Soft reconfig outbound update\n") - + /* Inbound soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft_in, clear_ip_bgp_all_soft_in_cmd, @@ -6160,7 +6230,7 @@ ALIAS (clear_bgp_as_in_prefix_filter, "Clear peers with the AS number\n" "Soft reconfig inbound update\n" "Push out prefix-list ORF and do inbound soft reconfig\n") - + /* Both soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft, clear_ip_bgp_all_soft_cmd, @@ -6534,7 +6604,7 @@ ALIAS (clear_bgp_as_soft, "Address family\n" "Clear peers with the AS number\n" "Soft reconfig\n") - + /* RS-client soft reconfiguration. */ #ifdef HAVE_IPV6 DEFUN (clear_bgp_all_rsclient, @@ -6867,7 +6937,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) int len; /* Header string for each address family. */ - static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd"; + static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd"; for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { @@ -7213,7 +7283,7 @@ DEFUN (show_ipv6_mbgp_summary, return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); } #endif /* HAVE_IPV6 */ - + const char * afi_safi_print (afi_t afi, safi_t safi) { @@ -7494,10 +7564,12 @@ bgp_show_peer (struct vty *vty, struct peer *p) /* Configured IP address. */ vty_out (vty, "BGP neighbor is %s, ", p->host); vty_out (vty, "remote AS %u, ", p->as); - vty_out (vty, "local AS %u%s, ", + vty_out (vty, "local AS %u%s%s, ", p->change_local_as ? p->change_local_as : p->local_as, CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? - " no-prepend" : ""); + " no-prepend" : "", + CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ? + " replace-as" : ""); vty_out (vty, "%s link%s", p->as == p->local_as ? "internal" : "external", VTY_NEWLINE); @@ -7790,6 +7862,12 @@ bgp_show_peer (struct vty *vty, struct peer *p) vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", p->ttl, VTY_NEWLINE); } + else + { + if (p->gtsm_hops > 0) + vty_out (vty, " Internal BGP neighbor may be up to %d hops away.%s", + p->gtsm_hops, VTY_NEWLINE); + } /* Local address. */ if (p->su_local) @@ -8115,7 +8193,7 @@ ALIAS (show_ip_bgp_instance_neighbors_peer, "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n") - + /* Show BGP's AS paths internal data. There are both `show ip bgp paths' and `show ip mbgp paths'. Those functions results are the same.*/ @@ -8148,7 +8226,7 @@ DEFUN (show_ip_bgp_ipv4_paths, return CMD_SUCCESS; } - + #include "hash.h" static void @@ -8191,7 +8269,7 @@ DEFUN (show_ip_bgp_attr_info, attr_show_all (vty); return CMD_SUCCESS; } - + static int bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient, afi_t afi, safi_t safi) @@ -8224,7 +8302,7 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient, vty_out (vty, "4 "); - vty_out (vty, "%11d ", rsclient->as); + vty_out (vty, "%10u ", rsclient->as); rmname = ROUTE_MAP_EXPORT_NAME(&rsclient->filter[afi][safi]); if ( rmname && strlen (rmname) > 13 ) @@ -8269,7 +8347,7 @@ bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp, int count = 0; /* Header string for each address family. */ - static char header[] = "Neighbor V AS Export-Policy Import-Policy Up/Down State"; + static char header[] = "Neighbor V AS Export-Policy Import-Policy Up/Down State"; for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, peer)) { @@ -8512,7 +8590,7 @@ ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary, "Summary of all Route Server Clients\n") #endif /* HAVE IPV6 */ - + /* Redistribute VTY commands. */ DEFUN (bgp_redistribute_ipv4, @@ -8726,7 +8804,7 @@ ALIAS (no_bgp_redistribute_ipv4_rmap_metric, "Default metric\n" "Route map reference\n" "Pointer to route-map entries\n") - + #ifdef HAVE_IPV6 DEFUN (bgp_redistribute_ipv6, bgp_redistribute_ipv6_cmd, @@ -8941,7 +9019,7 @@ ALIAS (no_bgp_redistribute_ipv6_rmap_metric, "Route map reference\n" "Pointer to route-map entries\n") #endif /* HAVE_IPV6 */ - + int bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int *write) @@ -8974,7 +9052,7 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, } return *write; } - + /* BGP node structure. */ static struct cmd_node bgp_node = { @@ -9017,7 +9095,7 @@ static struct cmd_node bgp_vpnv4_node = "%s(config-router-af)# ", 1 }; - + static void community_list_vty (void); void @@ -9137,6 +9215,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_bestpath_aspath_confed_cmd); install_element (BGP_NODE, &no_bgp_bestpath_aspath_confed_cmd); + /* "bgp bestpath as-path multipath-relax" commands */ + install_element (BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd); + /* "bgp log-neighbor-changes" commands */ install_element (BGP_NODE, &bgp_log_neighbor_changes_cmd); install_element (BGP_NODE, &no_bgp_log_neighbor_changes_cmd); @@ -9175,9 +9257,11 @@ bgp_vty_init (void) /* "neighbor local-as" commands. */ install_element (BGP_NODE, &neighbor_local_as_cmd); install_element (BGP_NODE, &neighbor_local_as_no_prepend_cmd); + install_element (BGP_NODE, &neighbor_local_as_no_prepend_replace_as_cmd); install_element (BGP_NODE, &no_neighbor_local_as_cmd); install_element (BGP_NODE, &no_neighbor_local_as_val_cmd); install_element (BGP_NODE, &no_neighbor_local_as_val2_cmd); + install_element (BGP_NODE, &no_neighbor_local_as_val3_cmd); /* "neighbor password" commands. */ install_element (BGP_NODE, &neighbor_password_cmd); @@ -10152,7 +10236,7 @@ bgp_vty_init (void) /* Community-list. */ community_list_vty (); } - + #include "memory.h" #include "bgp_regex.h" #include "bgp_clist.h" @@ -10551,7 +10635,7 @@ DEFUN (show_ip_community_list_arg, return CMD_SUCCESS; } - + static int extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv, int style, int reject_all_digit_name) @@ -10901,7 +10985,7 @@ DEFUN (show_ip_extcommunity_list_arg, return CMD_SUCCESS; } - + /* Return configuration string of community-list entry. */ static const char * community_list_config_str (struct community_entry *entry) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 60443830..f18d916f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -38,7 +38,7 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_mpath.h" - + /* All information about zebra. */ struct zclient *zclient = NULL; struct in_addr router_id_zebra; @@ -147,12 +147,11 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c)) bgp_connected_delete (c); - /* Fast external-failover (Currently IPv4 only) */ + /* Fast external-failover */ { struct listnode *mnode; struct bgp *bgp; struct peer *peer; - struct interface *peer_if; for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp)) { @@ -161,15 +160,10 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer->ttl != 1) - continue; - - if (peer->su.sa.sa_family == AF_INET) - peer_if = if_lookup_by_ipv4 (&peer->su.sin.sin_addr); - else + if ((peer->ttl != 1) && (peer->gtsm_hops != 1)) continue; - if (ifp == peer_if) + if (ifp == peer->nexthop.ifp) BGP_EVENT_ADD (peer, BGP_Stop); } } @@ -387,7 +381,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) return 0; } #endif /* HAVE_IPV6 */ - + struct interface * if_lookup_by_ipv4 (struct in_addr *addr) { @@ -534,6 +528,25 @@ if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr) } #endif /* HAVE_IPV6 */ +static int +if_get_ipv4_address (struct interface *ifp, struct in_addr *addr) +{ + struct listnode *cnode; + struct connected *connected; + struct prefix *cp; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + if ((cp->family == AF_INET) && !ipv4_martian(&(cp->u.prefix4))) + { + *addr = cp->u.prefix4; + return 1; + } + } + return 0; +} + int bgp_nexthop_set (union sockunion *local, union sockunion *remote, struct bgp_nexthop *nexthop, struct peer *peer) @@ -592,8 +605,9 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, { struct interface *direct = NULL; - /* IPv4 nexthop. I don't care about it. */ - if (peer->local_id.s_addr) + /* IPv4 nexthop. */ + ret = if_get_ipv4_address(ifp, &nexthop->v4); + if (!ret && peer->local_id.s_addr) nexthop->v4 = peer->local_id; /* Global address*/ @@ -929,7 +943,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) } #endif /* HAVE_IPV6 */ } - + /* Other routes redistribution into BGP. */ int bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) @@ -1055,7 +1069,7 @@ bgp_redistribute_metric_unset (struct bgp *bgp, afi_t afi, int type) return 1; } - + void bgp_zclient_reset (void) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 69c8c0a3..d72708e4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -61,7 +61,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - + /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -72,7 +72,7 @@ struct bgp_master *bm; /* BGP community-list. */ struct community_list_handler *bgp_clist; - + /* BGP global flag manipulation. */ int bgp_option_set (int flag) @@ -115,7 +115,7 @@ bgp_option_check (int flag) { return CHECK_FLAG (bm->options, flag); } - + /* BGP flag manipulation. */ int bgp_flag_set (struct bgp *bgp, int flag) @@ -136,7 +136,7 @@ bgp_flag_check (struct bgp *bgp, int flag) { return CHECK_FLAG (bgp->flags, flag); } - + /* Internal function to set BGP structure configureation flag. */ static void bgp_config_set (struct bgp *bgp, int config) @@ -155,7 +155,7 @@ bgp_config_check (struct bgp *bgp, int config) { return CHECK_FLAG (bgp->config, config); } - + /* Set BGP router identifier. */ int bgp_router_id_set (struct bgp *bgp, struct in_addr *id) @@ -175,7 +175,7 @@ bgp_router_id_set (struct bgp *bgp, struct in_addr *id) { IPV4_ADDR_COPY (&peer->local_id, id); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -205,7 +205,7 @@ bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) if (peer->sort != BGP_PEER_IBGP) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CLID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -233,7 +233,7 @@ bgp_cluster_id_unset (struct bgp *bgp) if (peer->sort != BGP_PEER_IBGP) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CLID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -242,7 +242,7 @@ bgp_cluster_id_unset (struct bgp *bgp) } return 0; } - + /* time_t value that is monotonicly increasing * and uneffected by adjustments to system clock */ @@ -273,7 +273,7 @@ bgp_timers_unset (struct bgp *bgp) return 0; } - + /* BGP confederation configuration. */ int bgp_confederation_id_set (struct bgp *bgp, as_t as) @@ -302,7 +302,7 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) if (peer_sort (peer) == BGP_PEER_EBGP) { peer->local_as = as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -322,7 +322,7 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) /* Reset the local_as to be our EBGP one */ if (peer_sort (peer) == BGP_PEER_EBGP) peer->local_as = as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -351,7 +351,7 @@ bgp_confederation_id_unset (struct bgp *bgp) if (peer_sort (peer) != BGP_PEER_IBGP) { peer->local_as = bgp->as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -415,7 +415,7 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as) if (peer->as == as) { peer->local_as = bgp->as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -471,7 +471,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as) if (peer->as == as) { peer->local_as = bgp->confed_id; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -485,7 +485,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as) return 0; } - + /* Local preference configuration. */ int bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref) @@ -508,7 +508,7 @@ bgp_default_local_preference_unset (struct bgp *bgp) return 0; } - + /* If peer is RSERVER_CLIENT in at least one address family and is not member of a peer_group for that family, return 1. Used to check wether the peer is included in list bgp->rsclient. */ @@ -832,6 +832,7 @@ peer_new (struct bgp *bgp) peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE); peer->obuf = stream_fifo_new (); peer->work = stream_new (BGP_MAX_PACKET_SIZE); + peer->scratch = stream_new (BGP_MAX_PACKET_SIZE); bgp_sync_init (peer); @@ -911,7 +912,7 @@ peer_as_change (struct peer *peer, as_t as) /* Stop peer. */ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -962,6 +963,7 @@ peer_as_change (struct peer *peer, as_t as) { peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); } } @@ -1026,9 +1028,9 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4) && afi == AFI_IP && safi == SAFI_UNICAST) - peer = peer_create (su, bgp, local_as, *as, 0, 0); + peer_create (su, bgp, local_as, *as, 0, 0); else - peer = peer_create (su, bgp, local_as, *as, afi, safi); + peer_create (su, bgp, local_as, *as, afi, safi); } return 0; @@ -1271,8 +1273,10 @@ peer_delete (struct peer *peer) stream_fifo_free (peer->obuf); if (peer->work) stream_free (peer->work); + if (peer->scratch) + stream_free(peer->scratch); peer->obuf = NULL; - peer->work = peer->ibuf = NULL; + peer->work = peer->scratch = peer->ibuf = NULL; /* Local and remote addresses. */ if (peer->su_local) @@ -1321,7 +1325,7 @@ peer_delete (struct peer *peer) return 0; } - + static int peer_group_cmp (struct peer_group *g1, struct peer_group *g2) { @@ -1456,13 +1460,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; /* password apply */ - if (peer->password) - XFREE (MTYPE_PEER_PASSWORD, peer->password); - - if (conf->password) + if (conf->password && !peer->password) peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password); - else - peer->password = NULL; bgp_md5_set (peer); @@ -1830,6 +1829,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, { group->conf->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); } } @@ -1872,7 +1872,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, peer_group2peer_config_copy (group, peer, afi, safi); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RMAP_BIND; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -1915,7 +1915,7 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer, peer_global_config_reset (peer); } - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RMAP_UNBIND; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -1926,7 +1926,19 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer, return 0; } - + + +static int +bgp_startup_timer_expire (struct thread *thread) +{ + struct bgp *bgp; + + bgp = THREAD_ARG (thread); + bgp->t_startup = NULL; + + return 0; +} + /* BGP instance creation by `router bgp' commands. */ static struct bgp * bgp_create (as_t *as, const char *name) @@ -1972,6 +1984,9 @@ bgp_create (as_t *as, const char *name) if (name) bgp->name = strdup (name); + THREAD_TIMER_ON (master, bgp->t_startup, bgp_startup_timer_expire, + bgp, bgp->restart_time); + return bgp; } @@ -2088,6 +2103,8 @@ bgp_delete (struct bgp *bgp) afi_t afi; int i; + THREAD_OFF (bgp->t_startup); + /* Delete static route. */ bgp_static_delete (bgp); @@ -2098,10 +2115,28 @@ bgp_delete (struct bgp *bgp) bgp_redistribute_unset (bgp, afi, i); for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) - peer_delete (peer); + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + + peer_delete (peer); + } for (ALL_LIST_ELEMENTS (bgp->group, node, next, group)) - peer_group_delete (group); + { + for (ALL_LIST_ELEMENTS (group->peer, node, next, peer)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + } + peer_group_delete (group); + } assert (listcount (bgp->rsclient) == 0); @@ -2163,7 +2198,7 @@ bgp_free (struct bgp *bgp) } XFREE (MTYPE_BGP, bgp); } - + struct peer * peer_lookup (struct bgp *bgp, union sockunion *su) { @@ -2232,7 +2267,7 @@ peer_lookup_with_open (union sockunion *su, as_t remote_as, } return NULL; } - + /* If peer is configured at least one address family return 1. */ int peer_active (struct peer *peer) @@ -2258,7 +2293,7 @@ peer_active_nego (struct peer *peer) return 1; return 0; } - + /* peer_flag_change_type. */ enum peer_change_type { @@ -2275,6 +2310,9 @@ peer_change_action (struct peer *peer, afi_t afi, safi_t safi, if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) return; + if (peer->status != Established) + return; + if (type == peer_change_reset) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); @@ -2334,6 +2372,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_SELF_ALL, 1, peer_change_reset_out }, { 0, 0, 0 } }; @@ -2409,7 +2448,7 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag) if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) peer_nsf_stop (peer); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); else @@ -2421,7 +2460,7 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag) BGP_EVENT_ADD (peer, BGP_Stop); } } - else if (peer->status == Established) + else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { if (flag == PEER_FLAG_DYNAMIC_CAPABILITY) peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; @@ -2674,7 +2713,7 @@ peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) { return peer_af_flag_modify (peer, afi, safi, flag, 0); } - + /* EBGP multihop configuration. */ int peer_ebgp_multihop_set (struct peer *peer, int ttl) @@ -2773,7 +2812,7 @@ peer_ebgp_multihop_unset (struct peer *peer) } return 0; } - + /* Neighbor description. */ int peer_description_set (struct peer *peer, char *desc) @@ -2796,7 +2835,7 @@ peer_description_unset (struct peer *peer) return 0; } - + /* Neighbor update-source. */ int peer_update_source_if_set (struct peer *peer, const char *ifname) @@ -2824,7 +2863,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2856,7 +2895,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2893,7 +2932,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2924,7 +2963,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su) peer->update_source = sockunion_dup (su); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2975,7 +3014,7 @@ peer_update_source_unset (struct peer *peer) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3005,7 +3044,7 @@ peer_update_source_unset (struct peer *peer) peer->update_if = NULL; } - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3016,7 +3055,7 @@ peer_update_source_unset (struct peer *peer) } return 0; } - + int peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, const char *rmap) @@ -3121,7 +3160,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int peer_port_set (struct peer *peer, u_int16_t port) { @@ -3135,7 +3174,7 @@ peer_port_unset (struct peer *peer) peer->port = BGP_PORT_DEFAULT; return 0; } - + /* neighbor weight. */ int peer_weight_set (struct peer *peer, u_int16_t weight) @@ -3183,7 +3222,7 @@ peer_weight_unset (struct peer *peer) } return 0; } - + int peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime) { @@ -3253,7 +3292,7 @@ peer_timers_unset (struct peer *peer) return 0; } - + int peer_timers_connect_set (struct peer *peer, u_int32_t connect) { @@ -3288,7 +3327,7 @@ peer_timers_connect_unset (struct peer *peer) return 0; } - + int peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv) { @@ -3321,7 +3360,7 @@ peer_advertise_interval_unset (struct peer *peer) return 0; } - + /* neighbor interface */ int peer_interface_set (struct peer *peer, const char *str) @@ -3342,7 +3381,7 @@ peer_interface_unset (struct peer *peer) return 0; } - + /* Allow-as in. */ int peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) @@ -3403,9 +3442,9 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int -peer_local_as_set (struct peer *peer, as_t as, int no_prepend) +peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as) { struct bgp *bgp = peer->bgp; struct peer_group *group; @@ -3421,9 +3460,14 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + if (peer->as == as) + return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS; + if (peer->change_local_as == as && ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend) - || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend))) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)) && + ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && replace_as) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && ! replace_as))) return 0; peer->change_local_as = as; @@ -3432,9 +3476,14 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) else UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + if (replace_as) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3455,7 +3504,12 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) else UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); - if (peer->status == Established) + if (replace_as) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3482,10 +3536,11 @@ peer_local_as_unset (struct peer *peer) peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3502,8 +3557,9 @@ peer_local_as_unset (struct peer *peer) { peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3514,7 +3570,7 @@ peer_local_as_unset (struct peer *peer) } return 0; } - + /* Set password for authenticating with the peer. */ int peer_password_set (struct peer *peer, const char *password) @@ -3537,8 +3593,8 @@ peer_password_set (struct peer *peer, const char *password) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3555,7 +3611,7 @@ peer_password_set (struct peer *peer, const char *password) peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3583,7 +3639,7 @@ peer_password_unset (struct peer *peer) && strcmp (peer->group->conf->password, peer->password) == 0) return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3606,7 +3662,7 @@ peer_password_unset (struct peer *peer) if (!peer->password) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3619,7 +3675,7 @@ peer_password_unset (struct peer *peer) return 0; } - + /* Set distribute list to the peer. */ int peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -3779,7 +3835,7 @@ peer_distribute_update (struct access_list *access) } } } - + /* Set prefix list to the peer. */ int peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -3938,7 +3994,7 @@ peer_prefix_list_update (struct prefix_list *plist) } } } - + int peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) @@ -4092,7 +4148,7 @@ peer_aslist_update (void) } } } - + /* Set route-map to the peer. */ int peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -4200,7 +4256,7 @@ peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) } return 0; } - + /* Set unsuppress-map to the peer. */ int peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, @@ -4282,7 +4338,7 @@ peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t max, u_char threshold, @@ -4377,21 +4433,46 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + +static int is_ebgp_multihop_configured (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + struct peer *peer1; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + if ((peer_sort(peer) != BGP_PEER_IBGP) && + (group->conf->ttl != 1)) + return 1; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if ((peer_sort (peer1) != BGP_PEER_IBGP) && + (peer1->ttl != 1)) + return 1; + } + } + else + { + if ((peer_sort(peer) != BGP_PEER_IBGP) && + (peer->ttl != 1)) + return 1; + } + return 0; +} + /* Set # of hops between us and BGP peer. */ int peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) { struct peer_group *group; struct listnode *node, *nnode; - struct peer *peer1; int ret; zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host); - if (peer->sort == BGP_PEER_IBGP) - return BGP_ERR_NO_IBGP_WITH_TTLHACK; - /* We cannot configure ttl-security hops when ebgp-multihop is already set. For non peer-groups, the check is simple. For peer-groups, it's slightly messy, because we need to check both the peer-group structure @@ -4400,38 +4481,23 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) mess of this configuration parameter, and OpenBGPD got it right. */ - if (peer->gtsm_hops == 0) { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - { - group = peer->group; - if (group->conf->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) - { - if (peer1->sort == BGP_PEER_IBGP) - continue; + if (peer->gtsm_hops == 0) + { + if (is_ebgp_multihop_configured (peer)) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - if (peer1->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - } - } - else - { - if (peer->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - } - /* specify MAXTTL on outgoing packets */ - ret = peer_ebgp_multihop_set (peer, MAXTTL); - if (ret != 0) - return ret; - } + /* specify MAXTTL on outgoing packets */ + /* Routine handles iBGP peers correctly */ + ret = peer_ebgp_multihop_set (peer, MAXTTL); + if (ret != 0) + return ret; + } peer->gtsm_hops = gtsm_hops; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) + if (peer->fd >= 0) sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops); } else @@ -4439,9 +4505,6 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) - continue; - peer->gtsm_hops = group->conf->gtsm_hops; /* Change setting of existing peer @@ -4476,9 +4539,6 @@ peer_ttl_security_hops_unset (struct peer *peer) zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host); - if (peer->sort == BGP_PEER_IBGP) - return 0; - /* if a peer-group member, then reset to peer-group default rather than 0 */ if (peer_group_active (peer)) peer->gtsm_hops = peer->group->conf->gtsm_hops; @@ -4488,7 +4548,7 @@ peer_ttl_security_hops_unset (struct peer *peer) opeer = peer; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) + if (peer->fd >= 0) sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0); } else @@ -4496,9 +4556,6 @@ peer_ttl_security_hops_unset (struct peer *peer) group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) - continue; - peer->gtsm_hops = 0; if (peer->fd >= 0) @@ -4508,7 +4565,7 @@ peer_ttl_security_hops_unset (struct peer *peer) return peer_ebgp_multihop_unset (opeer); } - + int peer_clear (struct peer *peer) { @@ -4529,7 +4586,7 @@ peer_clear (struct peer *peer) } peer->v_start = BGP_INIT_START_TIMER; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_RESET); else @@ -4613,7 +4670,7 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, } return 0; } - + /* Display peer uptime.*/ /* XXX: why does this function return char * when it takes buffer? */ char * @@ -4658,7 +4715,7 @@ peer_uptime (time_t uptime2, char *buf, size_t len) tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); return buf; } - + static void bgp_config_write_filter (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) @@ -4770,10 +4827,12 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, /* local-as. */ if (peer->change_local_as) if (! peer_group_active (peer)) - vty_out (vty, " neighbor %s local-as %u%s%s", addr, + vty_out (vty, " neighbor %s local-as %u%s%s%s", addr, peer->change_local_as, CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? - " no-prepend" : "", VTY_NEWLINE); + " no-prepend" : "", + CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ? + " replace-as" : "", VTY_NEWLINE); /* Description. */ if (peer->desc) @@ -4819,7 +4878,7 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, VTY_NEWLINE); /* ttl-security hops */ - if (peer->sort != BGP_PEER_IBGP && peer->gtsm_hops != 0) + if (peer->gtsm_hops != 0) if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops) vty_out (vty, " neighbor %s ttl-security hops %d%s", addr, peer->gtsm_hops, VTY_NEWLINE); @@ -4894,7 +4953,7 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, vty_out (vty, " neighbor %s strict-capability-match%s", addr, VTY_NEWLINE); - if (! peer_group_active (peer)) + if (! peer->af_group[AFI_IP][SAFI_UNICAST]) { if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) { @@ -4949,7 +5008,9 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, /* Nexthop self. */ if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF) && ! peer->af_group[afi][safi]) - vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE); + vty_out (vty, " neighbor %s next-hop-self%s%s", addr, + peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF_ALL) ? + " all" : "", VTY_NEWLINE); /* Remove private AS. */ if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) @@ -5260,6 +5321,9 @@ bgp_config_write (struct vty *vty) vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE); if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED)) vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + vty_out (vty, " bgp bestpath as-path multipath-relax%s", VTY_NEWLINE); + } if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID)) vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE); if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED) @@ -5350,7 +5414,7 @@ bgp_master_init (void) bm->start_time = bgp_clock (); } - + void bgp_init (void) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 63e326af..fbff82f3 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -104,6 +104,8 @@ struct bgp as_t *confed_peers; int confed_peers_cnt; + struct thread *t_startup; + /* BGP flags. */ u_int16_t flags; #define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0) @@ -120,6 +122,7 @@ struct bgp #define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11) #define BGP_FLAG_GRACEFUL_RESTART (1 << 12) #define BGP_FLAG_ASPATH_CONFED (1 << 13) +#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14) /* BGP Per AF flags */ u_int16_t af_flags[AFI_MAX][SAFI_MAX]; @@ -312,6 +315,12 @@ struct peer struct stream_fifo *obuf; struct stream *work; + /* We use a separate stream to encode MP_REACH_NLRI for efficient + * NLRI packing. peer->work stores all the other attributes. The + * actual packet is then constructed by concatenating the two. + */ + struct stream *scratch; + /* Status of the peer. */ int status; int ostatus; @@ -359,6 +368,8 @@ struct peer #define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */ #define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */ #define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */ +#define PEER_CAP_RESTART_BIT_ADV (1 << 9) /* sent restart state */ +#define PEER_CAP_RESTART_BIT_RCV (1 << 10) /* peer restart state */ /* Capability flags (reset in bgp_stop) */ u_int16_t af_cap[AFI_MAX][SAFI_MAX]; @@ -381,6 +392,7 @@ struct peer #define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */ #define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */ #define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */ +#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1 << 8) /* local-as no-prepend replace-as */ /* NSF mode (graceful restart) */ u_char nsf[AFI_MAX][SAFI_MAX]; @@ -404,6 +416,7 @@ struct peer #define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */ #define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */ #define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */ +#define PEER_FLAG_NEXTHOP_SELF_ALL (1 << 17) /* next-hop-self all */ /* MD5 password */ char *password; @@ -731,6 +744,7 @@ struct bgp_nlri /* BGP timers default value. */ #define BGP_INIT_START_TIMER 5 #define BGP_ERROR_START_TIMER 30 +#define BGP_LARGE_HOLDTIME 240 #define BGP_DEFAULT_HOLDTIME 180 #define BGP_DEFAULT_KEEPALIVE 60 #define BGP_DEFAULT_ASORIGINATE 15 @@ -776,6 +790,8 @@ enum bgp_clear_type /* Macros. */ #define BGP_INPUT(P) ((P)->ibuf) #define BGP_INPUT_PNT(P) (STREAM_PNT(BGP_INPUT(P))) +#define BGP_IS_VALID_STATE_FOR_NOTIF(S)\ + (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established)) /* Count prefix size from mask length */ #define PSIZE(a) (((a) + 7) / (8)) @@ -814,6 +830,7 @@ enum bgp_clear_type #define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK -30 #define BGP_ERR_NO_IBGP_WITH_TTLHACK -31 #define BGP_ERR_MAX -32 +#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -33 extern struct bgp_master *bm; @@ -843,7 +860,7 @@ extern struct peer *peer_create_accept (struct bgp *); extern char *peer_uptime (time_t, char *, size_t); extern int bgp_config_write (struct vty *); extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *); - + extern void bgp_master_init (void); extern void bgp_init (void); @@ -941,7 +958,7 @@ extern int peer_distribute_unset (struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_set (struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_unset (struct peer *, afi_t, safi_t); -extern int peer_local_as_set (struct peer *, as_t, int); +extern int peer_local_as_set (struct peer *, as_t, int, int); extern int peer_local_as_unset (struct peer *); extern int peer_prefix_list_set (struct peer *, afi_t, safi_t, int, const char *); diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 00000000..3bc25f23 --- /dev/null +++ b/buildtest.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# written 2012-2013 by David Lamparter, placed in Public Domain. +# +# builds some git commit of Quagga in some different configurations +# usage: buildtest.sh [commit [configurations...]] + +basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld --enable-pimd" + +configs_base="gcc|$basecfg" + +configs_ext="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" +configs_snmp="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology --enable-snmp" +configs_clang="clang|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" +configs_icc="icc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" + +defconfigs="base ext" +net-snmp-config --version &> /dev/null && defconfigs="$defconfigs snmp" +clang --version &> /dev/null && defconfigs="$defconfigs clang" +icc --version &> /dev/null && defconfigs="$defconfigs icc" + +echo "enabled configurations: $defconfigs" + +cc_gcc="CC=gcc; export CC" +cc_clang="CC=clang; export CC" +cc_icc="CC=icc; export CC" + +############################### + +errfunc() { + echo "something went wrong! check $TEMP" + exit 1 +} + +set -e +trap errfunc ERR + +COMMITREF="$1" +COMMITISH="`git rev-list --max-count=1 ${COMMITREF:-HEAD}`" +TEMP="`mktemp -t -d quaggabuild.XXXXXX`" +BASE="`pwd`" +CONFIGS="$2" + +echo using temporary directory: $TEMP +echo git commit used: +git --no-pager log -n 1 --pretty=oneline "$COMMITISH" + +cd "$TEMP" +git clone "$BASE" "source" +cd "source" +git checkout -b build "$COMMITISH" +git clean -d -f -x +sh bootstrap.sh + +cd .. + +echo -e "\n\n\n\n\033[33;1mmaking dist tarball\033[m" + +mkdir build_dist +cd build_dist +../source/configure +make distdir=sdist dist-gzip +cd .. +tar zxvf build_dist/sdist.tar.gz + +for cfg in ${CONFIGS:-$defconfigs}; do + echo -e "\n\n\n\n\033[33;1mbuilding configuration $cfg\033[m" + config="\${configs_$cfg}" + eval "config=$config" + + cc="${config%%|*}" + args="${config#*|}" + + ccset="\${cc_$cc}" + eval "ccset=$ccset" + eval "$ccset" + + bdir="build_$cfg" + mkdir "$bdir" + cd "$bdir" + ../sdist/configure $args + make -j5 + make check + make DESTDIR="$TEMP/inst_$cfg" install + cd .. +done + +echo -e "\n\n\n\neverything seems ok. you may now\n\trm -rf $TEMP" diff --git a/configure.ac b/configure.ac index 425fe273..60e9f66a 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,9 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.21, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.24.1, [https://bugzilla.quagga.net]) +CONFIG_ARGS="$*" +AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) @@ -19,8 +21,10 @@ AC_CANONICAL_HOST() AC_CANONICAL_TARGET() AM_INIT_AUTOMAKE(1.6) -AM_CONFIG_HEADER(config.h) +AM_SILENT_RULES([yes]) +AC_CONFIG_HEADERS(config.h) +AC_PATH_PROG(PERL, perl) AC_CHECK_PROG([GAWK],[gawk],[gawk],[not-in-PATH]) if test "x$GAWK" = "xnot-in-PATH" ; then AC_MSG_ERROR([GNU awk is required for lib/memtype.h made by memtypes.awk. @@ -31,7 +35,7 @@ AC_ARG_VAR([GAWK],[GNU AWK]) dnl default is to match previous behavior exampledir=${sysconfdir} AC_ARG_ENABLE([exampledir], - AC_HELP_STRING([--enable-exampledir], + AS_HELP_STRING([--enable-exampledir], [specify alternate directory for examples]), exampledir="$enableval",) dnl XXX add --exampledir to autoconf standard directory list somehow @@ -41,7 +45,7 @@ dnl default is to match previous behavior pkgsrcrcdir="" pkgsrcdir="" AC_ARG_ENABLE([pkgsrcrcdir], - AC_HELP_STRING([--enable-pkgsrcrcdir], + AS_HELP_STRING([--enable-pkgsrcrcdir], [specify directory for rc.d scripts]), pkgsrcrcdir="$enableval"; pkgsrcdir="pkgsrc",) dnl XXX add --pkgsrcrcdir to autoconf standard directory list somehow @@ -128,7 +132,7 @@ AC_MSG_CHECKING([whether to set a default CFLAGS]) if test "x${cflags_specified}" = "x" ; then case ${COMPILER} in "ICC") - CFLAGS="-Os -g -Wall -wd 279,869,981" + CFLAGS="-Os -g -Wall" AC_MSG_RESULT([Intel default]) ;; "GCC") @@ -192,86 +196,87 @@ dnl ---------------------- AC_ARG_WITH(pkg-extra-version, AS_HELP_STRING([--with-pkg-extra-version=VER], [add extra version field, for packagers/distributions]), [EXTRAVERSION=$withval],) +AC_ARG_WITH(pkg-git-version, + AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]), + [ test "x$withval" != "xno" && with_pkg_git_version="yes" ]) AC_ARG_ENABLE(vtysh, -[ --enable-vtysh include integrated vty shell for Quagga]) -AC_ARG_ENABLE(ipv6, -[ --disable-ipv6 turn off IPv6 related features and daemons]) + AS_HELP_STRING([--enable-vtysh], [include integrated vty shell for Quagga])) AC_ARG_ENABLE(doc, -[ --disable-doc do not build docs]) -AC_ARG_ENABLE(tests, -[ --disable-tests do not build tests]) + AS_HELP_STRING([--disable-doc], [do not build docs])) AC_ARG_ENABLE(zebra, -[ --disable-zebra do not build zebra daemon]) + AS_HELP_STRING([--disable-zebra], [do not build zebra daemon])) AC_ARG_ENABLE(bgpd, -[ --disable-bgpd do not build bgpd]) + AS_HELP_STRING([--disable-bgpd], [do not build bgpd])) AC_ARG_ENABLE(ripd, -[ --disable-ripd do not build ripd]) + AS_HELP_STRING([--disable-ripd], [do not build ripd])) AC_ARG_ENABLE(ripngd, -[ --disable-ripngd do not build ripngd]) + AS_HELP_STRING([--disable-ripngd], [do not build ripngd])) AC_ARG_ENABLE(ospfd, -[ --disable-ospfd do not build ospfd]) + AS_HELP_STRING([--disable-ospfd], [do not build ospfd])) AC_ARG_ENABLE(ospf6d, -[ --disable-ospf6d do not build ospf6d]) + AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d])) AC_ARG_ENABLE(babeld, -[ --disable-babeld do not build babeld]) + AS_HELP_STRING([--disable-babeld], [do not build babeld])) AC_ARG_ENABLE(watchquagga, -[ --disable-watchquagga do not build watchquagga]) + AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga])) AC_ARG_ENABLE(isisd, -[ --enable-isisd build isisd]) -AC_ARG_ENABLE(solaris, -[ --enable-solaris build solaris]) + AS_HELP_STRING([--disable-isisd], [do not build isisd])) +AC_ARG_ENABLE(pimd, + AS_HELP_STRING([--enable-pimd], [build pimd])) AC_ARG_ENABLE(bgp-announce, -[ --disable-bgp-announce, turn off BGP route announcement]) -AC_ARG_ENABLE(netlink, -[ --enable-netlink force to use Linux netlink interface]) -AC_ARG_ENABLE(broken-aliases, -[ --enable-broken-aliases enable aliases as distinct interfaces for Linux 2.2.X]) + AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(snmp, -[ --enable-snmp=ARG enable SNMP support (smux or agentx)]) + AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) AC_ARG_WITH(libpam, -[ --with-libpam use libpam for PAM support in vtysh]) + AS_HELP_STRING([--with-libpam], [use libpam for PAM support in vtysh])) AC_ARG_ENABLE(tcp-zebra, -[ --enable-tcp-zebra enable TCP/IP socket connection between zebra and protocol daemon]) + AS_HELP_STRING([--enable-tcp-zebra], [enable TCP/IP socket connection between zebra and protocol daemon])) AC_ARG_ENABLE(opaque-lsa, - AC_HELP_STRING([--disable-opaque-lsa],[do not build OSPF Opaque-LSA with OSPFAPI support (RFC2370)])) + AS_HELP_STRING([--disable-opaque-lsa],[do not build OSPF Opaque-LSA with OSPFAPI support (RFC2370)])) AC_ARG_ENABLE(ospfapi, -[ --disable-ospfapi do not build OSPFAPI to access the OSPF LSA Database]) + AS_HELP_STRING([--disable-ospfapi], [do not build OSPFAPI to access the OSPF LSA Database])) AC_ARG_ENABLE(ospfclient, -[ --disable-ospfclient do not build OSPFAPI client for OSPFAPI, - (this is the default if --disable-ospfapi is set)]) + AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI, + (this is the default if --disable-ospfapi is set)])) AC_ARG_ENABLE(ospf-te, - AC_HELP_STRING([--disable-ospf-te],[disable Traffic Engineering Extension to OSPF])) + AS_HELP_STRING([--disable-ospf-te],[disable Traffic Engineering Extension to OSPF])) AC_ARG_ENABLE(multipath, -[ --enable-multipath=ARG enable multipath function, ARG must be digit]) + AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit])) AC_ARG_ENABLE(user, - AC_HELP_STRING([--enable-user=user], [user to run Quagga suite as (default quagga)])) + AS_HELP_STRING([--enable-user=USER], [user to run Quagga suite as (default quagga)])) AC_ARG_ENABLE(group, - AC_HELP_STRING([--enable-group=group], [group to run Quagga suite as (default quagga)])) + AS_HELP_STRING([--enable-group=GROUP], [group to run Quagga suite as (default quagga)])) AC_ARG_ENABLE(vty_group, -[ --enable-vty-group=ARG set vty sockets to have specified group as owner]) + AS_HELP_STRING([--enable-vty-group=ARG], [set vty sockets to have specified group as owner])) AC_ARG_ENABLE(configfile_mask, -[ --enable-configfile-mask=ARG set mask for config files]) + AS_HELP_STRING([--enable-configfile-mask=ARG], [set mask for config files])) AC_ARG_ENABLE(logfile_mask, -[ --enable-logfile-mask=ARG set mask for log files]) + AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files])) AC_ARG_ENABLE(rtadv, -[ --disable-rtadv disable IPV6 router advertisement feature]) + AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) AC_ARG_ENABLE(irdp, -[ --enable-irdp enable IRDP server support in zebra]) + AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra])) AC_ARG_ENABLE(isis_topology, -[ --enable-isis-topology enable IS-IS topology generator]) + AS_HELP_STRING([--enable-isis-topology], [enable IS-IS topology generator])) AC_ARG_ENABLE(capabilities, -[ --disable-capabilities disable using POSIX capabilities]) + AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities])) +AC_ARG_ENABLE(rusage, + AS_HELP_STRING([--disable-rusage], [disable using getrusage])) AC_ARG_ENABLE(gcc_ultra_verbose, -[ --enable-gcc-ultra-verbose enable ultra verbose GCC warnings]) + AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings])) AC_ARG_ENABLE(linux24_tcp_md5, -[ --enable-linux24-tcp-md5 enable support for old, Linux-2.4 RFC2385 patch]) + AS_HELP_STRING([--enable-linux24-tcp-md5], [enable support for old, Linux-2.4 RFC2385 patch])) AC_ARG_ENABLE(gcc-rdynamic, -[ --enable-gcc-rdynamic enable gcc linking with -rdynamic for better backtraces]) + AS_HELP_STRING([--enable-gcc-rdynamic], [enable linking with -rdynamic for better backtraces (default if gcc)])) +AC_ARG_ENABLE(backtrace, + AS_HELP_STRING([--disable-backtrace,], [disable crash backtraces (default autodetect)])) AC_ARG_ENABLE(time-check, -[ --disable-time-check disable slow thread warning messages]) + AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages])) AC_ARG_ENABLE(pcreposix, -[ --enable-pcreposix enable using PCRE Posix libs for regex functions]) + AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) +AC_ARG_ENABLE(fpm, + AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support])) if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then CFLAGS="${CFLAGS} -W -Wcast-qual -Wstrict-prototypes" @@ -280,8 +285,10 @@ if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then CFLAGS="${CFLAGS} -Wpacked -Wpadded" fi -if test x"${enable_gcc_rdynamic}" = x"yes" ; then - LDFLAGS="${LDFLAGS} -rdynamic" +if test x"${enable_gcc_rdynamic}" != x"no" ; then + if test x"${enable_gcc_rdynamic}" = x"yes" -o x"$COMPILER" = x"GCC"; then + LDFLAGS="${LDFLAGS} -rdynamic" + fi fi if test x"${enable_time_check}" != x"no" ; then @@ -292,13 +299,8 @@ if test x"${enable_time_check}" != x"no" ; then fi fi -if test "${enable_broken_aliases}" = "yes"; then - if test "${enable_netlink}" = "yes" - then - AC_MSG_FAILURE([Sorry you can not use netlink with broken aliases]) - fi - AC_DEFINE(HAVE_BROKEN_ALIASES,,Broken Alias) - enable_netlink=no +if test "${enable_fpm}" = "yes"; then + AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) fi if test "${enable_tcp_zebra}" = "yes"; then @@ -330,7 +332,7 @@ if test "${enable_irdp}" = "yes"; then AC_DEFINE(HAVE_IRDP,, IRDP ) fi -if test "${enable_isisd}" = "yes" && test "${enable_isis_topology}" = yes; then +if test "${enable_isisd}" != "no" && test "${enable_isis_topology}" = yes; then AC_DEFINE(TOPOLOGY_GENERATE,,Enable IS-IS topology generator code) ISIS_TOPOLOGY_INCLUDES="-I\$(srcdir)/topology" ISIS_TOPOLOGY_DIR="topology" @@ -392,10 +394,19 @@ dnl Add extra version string to package dnl name, string and version fields. dnl ----------------------------------- if test "x${EXTRAVERSION}" != "x" ; then - VERSION=${VERSION}${EXTRAVERSION} - PACKAGE_VERSION=${PACKAGE_VERSION}${EXTRAVERSION} - PACKAGE_STRING=${PACKAGE_STRING}${EXTRAVERSION} + VERSION="${VERSION}${EXTRAVERSION}" + PACKAGE_VERSION="${PACKAGE_VERSION}${EXTRAVERSION}" + PACKAGE_STRING="${PACKAGE_STRING}${EXTRAVERSION}" +fi + +if test "x$with_pkg_git_version" = "xyes"; then + if test -d "${srcdir}/.git"; then + AC_DEFINE(GIT_VERSION, [1], [include git version info]) + else with_pkg_git_version="no" + AC_MSG_WARN([--with-pkg-git-version given, but this is not a git checkout]) + fi fi +AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" = "xyes"]) dnl ------------------------------------ dnl Check C keywords and standard types @@ -419,7 +430,7 @@ dnl Check other header files. dnl ------------------------- AC_CHECK_HEADERS([stropts.h sys/ksym.h sys/times.h sys/select.h \ sys/types.h linux/version.h netdb.h asm/types.h \ - sys/param.h limits.h signal.h \ + sys/cdefs.h sys/param.h limits.h signal.h \ sys/socket.h netinet/in.h time.h sys/time.h]) dnl Utility macro to avoid retyping includes all the time @@ -451,6 +462,9 @@ m4_define([QUAGGA_INCLUDES], #if HAVE_SYS_SOCKET_H # include #endif +#ifdef __APPLE__ +# define __APPLE_USE_RFC_3542 +#endif #if HAVE_NETINET_IN_H # include #endif @@ -507,6 +521,18 @@ AC_CHECK_HEADERS([ucontext.h], [], [], QUAGGA_INCLUDES ]) +m4_define([UCONTEXT_INCLUDES], +[#include ])dnl + +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.uc_regs], + [], [], [UCONTEXT_INCLUDES]) +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs], + [AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs.nip], + [], [], [UCONTEXT_INCLUDES])], + [], [UCONTEXT_INCLUDES]) +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.gregs], + [], [], [UCONTEXT_INCLUDES]) + m4_define([QUAGGA_INCLUDES], QUAGGA_INCLUDES [#if HAVE_SYS_UN_H @@ -549,6 +575,7 @@ case "$host" in AC_DEFINE(SUNOS_5, 1, SunOS 5) AC_CHECK_LIB(xnet, main) CURSES=-lcurses + SOLARIS="solaris" ;; [*-sunos5.[8-9]] \ | [*-sunos5.1[0-9]] \ @@ -567,34 +594,23 @@ case "$host" in AC_DEFINE([HAVE_STACK_TRACE],1,[Stack symbols decode functionality]) ]) CURSES=-lcurses + SOLARIS="solaris" ;; *-sunos5* | *-solaris2*) AC_DEFINE(SUNOS_5,,SunOS 5, Unknown SunOS) AC_CHECK_LIB(socket, main) AC_CHECK_LIB(nsl, main) CURSES=-lcurses + SOLARIS="solaris" ;; *-linux*) opsys=gnu-linux AC_DEFINE(GNU_LINUX,,GNU Linux) ;; - *-nec-sysv4*) - AC_CHECK_LIB(nsl, gethostbyname) - AC_CHECK_LIB(socket, socket) - ;; *-openbsd*) opsys=openbsd AC_DEFINE(OPEN_BSD,,OpenBSD) ;; - *-bsdi*) - opsys=bsdi - OTHER_METHOD="mtu_kvm.o" - AC_CHECK_LIB(kvm, main) - ;; - *-irix6.5) - opsys=irix - AC_DEFINE(IRIX_65,,IRIX 6.5) - ;; esac AC_SYS_LARGEFILE @@ -605,7 +621,6 @@ dnl --------------------- case "${enable_vtysh}" in "yes") VTYSH="vtysh"; AC_DEFINE(VTYSH,,VTY shell) - AC_PATH_PROG(PERL, perl) dnl Vtysh uses libreadline, which looks for termcap functions at dnl configure time. We follow readlines search order. dnl The required procedures are in libtermcap on NetBSD, in @@ -775,59 +790,18 @@ dnl Determine routing get and set method dnl ------------------------------------ AC_MSG_CHECKING(zebra between kernel interface method) if test x"$opsys" = x"gnu-linux"; then - if test "${enable_netlink}" = "yes";then - AC_MSG_RESULT(netlink) - RT_METHOD=rt_netlink.o - AC_DEFINE(HAVE_NETLINK,,netlink) - netlink=yes - elif test "${enable_netlink}" = "no"; then - AC_MSG_RESULT(ioctl) - RT_METHOD=rt_ioctl.o - netlink=no - else - AC_MSG_RESULT(netlink) - RT_METHOD=rt_netlink.o - AC_DEFINE(HAVE_NETLINK,,netlink) - netlink=yes - fi -elif test x"$opsys" = x"sol2-6";then - AC_MSG_RESULT(Route socket) - KERNEL_METHOD="kernel_socket.o" - RT_METHOD="rt_socket.o" -elif test x"$opsys" = x"sol8";then - AC_MSG_RESULT(Route socket) - KERNEL_METHOD="kernel_socket.o" - RT_METHOD="rt_socket.o" -elif test "$opsys" = "irix" ; then + AC_MSG_RESULT(netlink) + RT_METHOD=rt_netlink.o + AC_DEFINE(HAVE_NETLINK,,netlink) + netlink=yes +else AC_MSG_RESULT(Route socket) KERNEL_METHOD="kernel_socket.o" RT_METHOD="rt_socket.o" -else - AC_TRY_RUN([#include -#include -#include - -main () -{ - int ac_sock; - - ac_sock = socket (AF_ROUTE, SOCK_RAW, 0); - if (ac_sock < 0 && errno == EINVAL) - exit (1); - exit (0); -}], - [KERNEL_METHOD=kernel_socket.o - RT_METHOD=rt_socket.o - AC_MSG_RESULT(socket)], - [RT_METHOD=rt_ioctl.o - AC_MSG_RESULT(ioctl)], - [KERNEL_METHOD=kernel_socket.o - RT_METHOD=rt_socket.o - AC_MSG_RESULT(socket)]) fi AC_SUBST(RT_METHOD) AC_SUBST(KERNEL_METHOD) -AC_SUBST(OTHER_METHOD) +AM_CONDITIONAL([HAVE_NETLINK], [test "x$netlink" = "xyes"]) dnl -------------------------- dnl Determine IS-IS I/O method @@ -901,12 +875,11 @@ AC_CACHE_CHECK([route read method], [quagga_cv_rtread_method], [if test "x$netlink" = xyes; then quagga_cv_rtread_method="netlink" else -for quagga_cv_rtread_method in /proc/net/route /dev/ip /dev/null; +for quagga_cv_rtread_method in /dev/ip /dev/null; do test x`ls $quagga_cv_rtread_method 2>/dev/null` = x"$quagga_cv_rtread_method" && break done case $quagga_cv_rtread_method in - "/proc/net/route") quagga_cv_rtread_method="proc";; "/dev/ip") case "$host" in *-freebsd*) quagga_cv_rtread_method="sysctl";; @@ -934,9 +907,6 @@ elif test "$opsys" = "sol8";then AC_MSG_RESULT(Solaris GLIF) IF_METHOD=if_ioctl_solaris.o IOCTL_METHOD=ioctl_solaris.o -elif test "$opsys" = "irix" ; then - AC_MSG_RESULT(IRIX) - IF_METHOD=if_ioctl.o elif test "$opsys" = "openbsd";then AC_MSG_RESULT(openbsd) IF_METHOD=if_ioctl.o @@ -959,7 +929,7 @@ AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], [], QUAGGA_INCLUDES) AC_MSG_CHECKING([for BSD struct ip_mreq hack]) AC_TRY_COMPILE([#ifdef HAVE_SYS_PARAM_H #include -#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) +#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__sun) return (0); #else #error No support for BSD struct ip_mreq hack detected @@ -1023,21 +993,6 @@ if test $ac_cv_have_decl_TCP_MD5SIG = no; then AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)]) fi -dnl ----------------------- -dnl check proc file system. -dnl ----------------------- -if test "$netlink" != yes; then - if test -r /proc/net/dev; then - AC_DEFINE(HAVE_PROC_NET_DEV,,/proc/net/dev) - IF_PROC=if_proc.o - fi - if test -r /proc/net/if_inet6; then - AC_DEFINE(HAVE_PROC_NET_IF_INET6,,/proc/net/if_inet6) - IF_PROC=if_proc.o - fi -fi -AC_SUBST(IF_PROC) - dnl ----------------------------- dnl check ipforward detect method dnl ----------------------------- @@ -1058,7 +1013,6 @@ case $quagga_cv_ipforward_method in "/proc/net/snmp") quagga_cv_ipforward_method="proc";; "/dev/ip") case "$host" in - *-nec-sysv4*) quagga_cv_ipforward_method="ews";; *-freebsd*) quagga_cv_ipforward_method="sysctl";; *) quagga_cv_ipforward_method="solaris";; esac;; @@ -1073,150 +1027,37 @@ dnl ---------- dnl IPv6 check dnl ---------- AC_MSG_CHECKING(whether does this OS have IPv6 stack) -if test "${enable_ipv6}" = "no"; then - AC_MSG_RESULT(disabled) -else -dnl ---------- -dnl INRIA IPv6 -dnl ---------- - if grep IPV6_INRIA_VERSION /usr/include/netinet/in.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,INRIA IPv6) - AC_DEFINE(INRIA_IPV6,1,INRIA IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" - LIB_IPV6="" - AC_MSG_RESULT(INRIA IPv6) dnl --------- dnl KAME IPv6 dnl --------- - elif grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,KAME IPv6) + if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then AC_DEFINE(KAME,1,KAME IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test -d /usr/local/v6/lib -a -f /usr/local/v6/lib/libinet6.a; then - LIB_IPV6="-L/usr/local/v6/lib -linet6" - fi AC_MSG_RESULT(KAME) -dnl ------------------------- -dnl MUSICA IPv6 -dnl default host check -dnl It is not used by Kheops -dnl ------------------------- - elif grep MUSICA /usr/include6/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,Musicia IPv6) - AC_DEFINE(MUSICA,1,Musica IPv6 stack) - AC_DEFINE(KAME,1,KAME IPv6 stack) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test -d /usr/local/v6/lib -a -f /usr/local/v6/lib/libinet6.a; then - LIB_IPV6="-L/usr/local/v6/lib -linet6" - fi - AC_MSG_RESULT(MUSICA) -dnl --------- -dnl NRL check -dnl --------- - elif grep NRL /usr/include/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,NRL IPv6) - AC_DEFINE(NRL,1,NRL) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test x"$opsys" = x"bsdi";then - AC_DEFINE(BSDI_NRL,,BSDI) - AC_MSG_RESULT(BSDI_NRL) - else - AC_MSG_RESULT(NRL) - fi dnl ------------------------------------ dnl Solaris 9, 10 and potentially higher dnl ------------------------------------ elif test x"$opsys" = x"sol8"; then - zebra_cv_ipv6=yes; - AC_DEFINE(HAVE_IPV6, 1, IPv6) AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" AC_MSG_RESULT(Solaris IPv6) dnl ---------- dnl Linux IPv6 dnl ---------- - elif test "${enable_ipv6}" = "yes"; then - AC_EGREP_CPP(yes, [ - #include - /* 2.1.128 or later */ - #if LINUX_VERSION_CODE >= 0x020180 - yes - #endif], - [zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes - AC_MSG_RESULT(Linux IPv6)]) + elif test x"$opsys" = x"gnu-linux"; then + AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) + AC_MSG_RESULT(Linux IPv6) else - if test x`ls /proc/net/ipv6_route 2>/dev/null` = x"/proc/net/ipv6_route" - then - zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes - AC_MSG_RESULT(Linux IPv6) - fi + AC_MSG_ERROR([Failed to detect IPv6 stack]) fi - if test "$zebra_cv_linux_ipv6" = "yes";then - AC_MSG_CHECKING(whether libc has IPv6 support) - AC_TRY_LINK([#include - ],[ int a; a = (int) in6addr_any.s6_addr[0]; if (a != 12345) return a; ], - [AC_MSG_RESULT(yes) - zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes], - [AC_MSG_RESULT(no) - zebra_cv_ipv6=no - zebra_cv_linux_ipv6=no]) - fi - - if test "$zebra_cv_linux_ipv6" = "yes";then - AC_MSG_CHECKING(for GNU libc >= 2.1) - AC_DEFINE(HAVE_IPV6,1,Linux IPv6) - AC_EGREP_CPP(yes, [ -#include -#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 - yes -#endif], - [glibc=yes - AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no) - ) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test "$glibc" != "yes"; then - INCLUDES="-I/usr/inet6/include" - if test x`ls /usr/inet6/lib/libinet6.a 2>/dev/null` != x;then - LIB_IPV6="-L/usr/inet6/lib -linet6" - fi - fi - fi - -dnl ----------------------- -dnl Set IPv6 related values -dnl ----------------------- - LIBS="$LIB_IPV6 $LIBS" - AC_SUBST(LIB_IPV6) - - if test x"$RIPNGD" = x""; then - AC_MSG_RESULT(IPv4 only) - fi -fi +dnl this is unconditial, for compatibility +AC_DEFINE(HAVE_IPV6,1,IPv6) dnl ------------------ dnl IPv6 header checks dnl ------------------ -if test "x${zebra_cv_ipv6}" = "xyes"; then AC_CHECK_HEADERS([netinet6/in6.h netinet/in6_var.h netinet/icmp6.h \ netinet6/in6_var.h netinet6/nd6.h], [], [], QUAGGA_INCLUDES) -fi m4_define([QUAGGA_INCLUDES],dnl QUAGGA_INCLUDES @@ -1244,13 +1085,6 @@ else DOC="doc" fi -dnl can't use TESTS as name, that's special with automake -if test "${enable_tests}" = "no";then - BUILD_TESTS="" -else - BUILD_TESTS="tests" -fi - dnl -------------------- dnl Daemon disable check dnl -------------------- @@ -1310,32 +1144,29 @@ fi AM_CONDITIONAL(OSPFCLIENT, test "x$OSPFCLIENT" = "xospfclient") case "${enable_ripngd}" in - "yes") RIPNGD="ripngd";; "no" ) RIPNGD="";; - * ) ;; + * ) RIPNGD="ripngd";; esac AM_CONDITIONAL(RIPNGD, test "x$RIPNGD" = "xripngd") case "${enable_ospf6d}" in - "yes") OSPF6D="ospf6d";; "no" ) OSPF6D="";; - * ) ;; + * ) OSPF6D="ospf6d";; esac AM_CONDITIONAL(OSPF6D, test "x$OSPF6D" = "xospf6d") case "${enable_isisd}" in - "yes") ISISD="isisd";; "no" ) ISISD="";; - * ) ;; + * ) ISISD="isisd";; esac AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd") -# XXX Perhaps auto-enable on Solaris, but that's messy for cross builds. -case "${enable_solaris}" in - "yes") SOLARIS="solaris";; - "no" ) SOLARIS="";; +case "${enable_pimd}" in + "yes") PIMD="pimd";; + "no" ) PIMD="";; * ) ;; esac +AM_CONDITIONAL(PIMD, test "x$PIMD" = "xpimd") if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) @@ -1344,7 +1175,6 @@ else fi AC_SUBST(DOC) -AC_SUBST(BUILD_TESTS) AC_SUBST(ZEBRA) AC_SUBST(BGPD) AC_SUBST(RIPD) @@ -1354,9 +1184,9 @@ AC_SUBST(OSPF6D) AC_SUBST(BABELD) AC_SUBST(WATCHQUAGGA) AC_SUBST(ISISD) +AC_SUBST(PIMD) AC_SUBST(SOLARIS) AC_SUBST(VTYSH) -AC_SUBST(INCLUDES) AC_SUBST(CURSES) AC_SUBST(OSPFCLIENT) AC_SUBST(OSPFAPI) @@ -1365,13 +1195,6 @@ AC_CHECK_LIB(c, inet_pton, [AC_DEFINE(HAVE_INET_PTON,,inet_pton)]) AC_CHECK_LIB(crypt, crypt) AC_CHECK_LIB(resolv, res_init) -dnl --------------------------------------------------- -dnl BSD/OS 4.1 define inet_XtoY function as __inet_XtoY -dnl --------------------------------------------------- -AC_CHECK_FUNC(__inet_ntop, AC_DEFINE(HAVE_INET_NTOP,,__inet_ntop)) -AC_CHECK_FUNC(__inet_pton, AC_DEFINE(HAVE_INET_PTON,,__inet_pton)) -AC_CHECK_FUNC(__inet_aton, AC_DEFINE(HAVE_INET_ATON,,__inet_aton)) - dnl --------------------------- dnl check system has PCRE regexp dnl --------------------------- @@ -1436,7 +1259,8 @@ dnl sockaddr and netinet checks dnl --------------------------- AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in, struct sockaddr_in6, struct sockaddr_un, struct sockaddr_dl, - socklen_t, + socklen_t, struct vifctl, struct mfcctl, struct sioc_sg_req, + vifi_t, struct sioc_vif_req, struct igmpmsg, struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq, struct nd_opt_adv_interval, struct rt_addrinfo, struct nd_opt_homeagent_info, struct nd_opt_adv_interval], @@ -1445,6 +1269,7 @@ AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in, AC_CHECK_MEMBERS([struct sockaddr.sa_len, struct sockaddr_in.sin_len, struct sockaddr_un.sun_len, struct sockaddr_in6.sin6_scope_id, + struct sockaddr_dl.sdl_len, struct if6_aliasreq.ifra_lifetime, struct nd_opt_adv_interval.nd_opt_ai_type], [], [], QUAGGA_INCLUDES) @@ -1464,15 +1289,55 @@ AC_CHECK_TYPES([struct in_pktinfo], AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!']) fi], [QUAGGA_INCLUDES]) +dnl ----------------------- +dnl checking for IP_PKTINFO +dnl ----------------------- +AC_MSG_CHECKING(for IP_PKTINFO) +AC_TRY_COMPILE([#include ], [ + int opt = IP_PKTINFO; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_PKTINFO, 1, [Have IP_PKTINFO]) +], [ + AC_MSG_RESULT(no) +]) + +dnl --------------------------- +dnl checking for IP_RECVDSTADDR +dnl --------------------------- +AC_MSG_CHECKING(for IP_RECVDSTADDR) +AC_TRY_COMPILE([#include ], [ + int opt = IP_RECVDSTADDR; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_RECVDSTADDR, 1, [Have IP_RECVDSTADDR]) +], [ + AC_MSG_RESULT(no) +]) + +dnl ---------------------- +dnl checking for IP_RECVIF +dnl ---------------------- +AC_MSG_CHECKING(for IP_RECVIF) +AC_TRY_COMPILE([#include ], [ + int opt = IP_RECVIF; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_RECVIF, 1, [Have IP_RECVIF]) +], [ + AC_MSG_RESULT(no) +]) + dnl -------------------------------------- dnl checking for getrusage struct and call dnl -------------------------------------- -AC_MSG_CHECKING(whether getrusage is available) -AC_TRY_COMPILE([#include -],[struct rusage ac_x; getrusage (RUSAGE_SELF, &ac_x);], -[AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_RUSAGE,,rusage)], - AC_MSG_RESULT(no)) +if test "${enable_rusage}" != "no"; then + AC_MSG_CHECKING(whether getrusage is available) + AC_TRY_COMPILE([#include ],[struct rusage ac_x; getrusage (RUSAGE_SELF, &ac_x);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_RUSAGE,,rusage)], + AC_MSG_RESULT(no)) +fi dnl -------------------------------------- dnl checking for clock_time monotonic struct and call @@ -1521,15 +1386,40 @@ if test "${enable_capabilities}" != "no"; then fi AC_SUBST(LIBCAP) +dnl --------------------------------------------------------------------------- +dnl http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html +dnl Check for and set one of the following = 1 +dnl HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +dnl HAVE_SYS_WEAK_ALIAS_PRAGMA +dnl HAVE_SYS_WEAK_ALIAS_HPSECONDARY +dnl HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE +dnl If any scheme is found, set +dnl HAVE_SYS_WEAK_ALIAS=1 +dnl The following variable is set to text value +dnl WEAK_ALIAS = "attribute" || "pragma" || "hpsecondary" || "criduplicate" || "no" +dnl If weak alias can cross object file boundaries +dnl WEAK_ALIAS_CROSSFILE = "yes" || "no" +dnl --------------------------------------------------------------------------- +AX_SYS_WEAK_ALIAS + dnl --------------------------- dnl check for glibc 'backtrace' dnl --------------------------- -AC_CHECK_HEADER([execinfo.h], - [AC_CHECK_FUNC([backtrace], - [AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace]) - AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding]) +if test x"${enable_backtrace}" != x"no" ; then + backtrace_ok=no + AC_CHECK_HEADER([execinfo.h], [ + AC_SEARCH_LIBS([backtrace], [execinfo], [ + AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace]) + AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding]) + backtrace_ok=yes + ],, [-lm]) ]) -]) + + if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then + dnl user explicitly requested backtrace but we failed to find support + AC_MSG_FAILURE([failed to find backtrace support]) + fi +fi dnl ----------------------------------------- dnl check for malloc mallinfo struct and call @@ -1577,6 +1467,18 @@ fi AC_SUBST(PICFLAGS) AC_SUBST(PILDFLAGS) +dnl ------- +dnl DejaGNU +dnl ------- +if test x"$DEJAGNU" = x +then + DEJAGNU="\$(top_srcdir)/tests/global-conf.exp" +fi +RUNTESTDEFAULTFLAGS="-x --tool \$\$tool" + +AC_SUBST(DEJAGNU) +AC_SUBST(RUNTESTDEFAULTFLAGS) + dnl ------------------------------ dnl set paths for state directory dnl ------------------------------ @@ -1615,6 +1517,7 @@ AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID) AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID) AC_DEFINE_UNQUOTED(PATH_BABELD_PID, "$quagga_statedir/babeld.pid",babeld PID) AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID) +AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID) AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID) AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$quagga_statedir/zserv.api",zebra api socket) AC_DEFINE_UNQUOTED(ZEBRA_VTYSH_PATH, "$quagga_statedir/zebra.vty",zebra vty socket) @@ -1625,6 +1528,7 @@ AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socke AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket) AC_DEFINE_UNQUOTED(BABEL_VTYSH_PATH, "$quagga_statedir/babeld.vty",babeld vty socket) AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket) +AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket) AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory) dnl ------------------------------- @@ -1649,8 +1553,11 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile - doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile - redhat/Makefile + doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile + pimd/Makefile + tests/bgpd.tests/Makefile + tests/libzebra.tests/Makefile + redhat/Makefile pkgsrc/Makefile redhat/quagga.spec lib/version.h @@ -1674,7 +1581,6 @@ source code location : ${srcdir} compiler : ${CC} compiler flags : ${CFLAGS} make : ${MAKE-make} -includes : ${INCLUDES} linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${quagga_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` diff --git a/doc/Makefile.am b/doc/Makefile.am index f58657b9..11eca73f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -44,7 +44,7 @@ info_TEXINFOS = quagga.texi # because it cant just work from the png's directly it seems - contrary # to the documentation... quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS) - $(TEXI2PDF) -o "$@" $< + $(TEXI2PDF) -o "$@" $< || true quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \ @@ -61,7 +61,11 @@ quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ .dia.png: $(DIATOPNG) "$@" $< -man_MANS = +man_MANS = + +if PIMD +man_MANS += pimd.8 +endif if BGPD man_MANS += bgpd.8 @@ -105,7 +109,7 @@ endif EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \ bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ripd.8 \ - ripngd.8 vtysh.1 watchquagga.8 zebra.8 \ + ripngd.8 pimd.8 vtysh.1 watchquagga.8 zebra.8 \ mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \ mpls/opaque_lsa.txt mpls/ospfd.conf \ $(figures_sources) $(figures_png) $(figures_txt) diff --git a/doc/basic.texi b/doc/basic.texi index b3b23ca9..0f7bec9c 100644 --- a/doc/basic.texi +++ b/doc/basic.texi @@ -15,8 +15,8 @@ The following sections discuss commands common to all the routing daemons. @menu -* Terminal Mode Commands:: Common commands used in a VTY * Config Commands:: Commands used in config files +* Terminal Mode Commands:: Common commands used in a VTY * Common Invocation Options:: Starting the daemons * Virtual Terminal Interfaces:: Interacting with the daemons @end menu @@ -580,8 +580,8 @@ Move up to previous line in the history buffer. @kindex @key{TAB} Use command line completion by typing @key{TAB}. -@item -@kindex ? +@item ? +@kindex @key{?} You can use command line help by typing @code{help} at the beginning of the line. Typing @kbd{?} at any point in the line will show possible completions. diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 63834600..7d92b5e1 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -124,6 +124,12 @@ sequences should should be taken into account during the BGP best path decision process. @end deffn +@deffn {BGP} {bgp bestpath as-path multipath-relax} {} +This command specifies that BGP decision process should consider paths +of equal AS_PATH length candidates for multipath computation. Without +the knob, the entire AS_PATH must match for multipath computation. +@end deffn + @node BGP route flap dampening @subsection BGP route flap dampening @@ -293,10 +299,12 @@ This command is deprecated and may be removed in a future release. Its use should be avoided. @end deffn -@deffn {BGP} {neighbor @var{peer} next-hop-self} {} -@deffnx {BGP} {no neighbor @var{peer} next-hop-self} {} +@deffn {BGP} {neighbor @var{peer} next-hop-self [all]} {} +@deffnx {BGP} {no neighbor @var{peer} next-hop-self [all]} {} This command specifies an announced route's nexthop as being equivalent -to the address of the bgp router. +to the address of the bgp router if it is learned via eBGP. +If the optional keyword @code{all} is specified the modifiation is done +also for routes learned via iBGP. @end deffn @deffn {BGP} {neighbor @var{peer} update-source @var{}} {} @@ -339,6 +347,35 @@ routes. @deffnx {BGP} {no neighbor @var{peer} maximum-prefix @var{number}} {} @end deffn +@deffn {BGP} {neighbor @var{peer} local-as @var{as-number}} {} +@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend} {} +@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend replace-as} {} +@deffnx {BGP} {no neighbor @var{peer} local-as} {} +Specify an alternate AS for this BGP process when interacting with the +specified peer. With no modifiers, the specified local-as is prepended to +the received AS_PATH when receiving routing updates from the peer, and +prepended to the outgoing AS_PATH (after the process local AS) when +transmitting local routes to the peer. + +If the no-prepend attribute is specified, then the supplied local-as is not +prepended to the received AS_PATH. + +If the replace-as attribute is specified, then only the supplied local-as is +prepended to the AS_PATH when transmitting local-route updates to this peer. + +Note that replace-as can only be specified if no-prepend is. + +This command is only allowed for eBGP peers. +@end deffn + +@deffn {BGP} {neighbor @var{peer} ttl-security hops @var{number}} {} +@deffnx {BGP} {no neighbor @var{peer} ttl-security hops @var{number}} {} +This command enforces Generalized TTL Security Mechanism (GTSM), as +specified in RFC 5082. With this command, only neighbors that are the +specified number of hops away will be allowed to become neighbors. This +command is mututally exclusive with @command{ebgp-multihop}. +@end deffn + @node Peer filtering @subsection Peer filtering @@ -459,6 +496,11 @@ This command defines a new AS path access list. @end deffn @deffn {Route Map} {set as-path prepend @var{as-path}} {} +Prepend the given string of AS numbers to the AS_PATH. +@end deffn + +@deffn {Route Map} {set as-path prepend last-as @var{num}} {} +Prepend the existing last AS number (the leftmost ASN) to the AS_PATH. @end deffn @node Private AS Numbers diff --git a/doc/install.texi b/doc/install.texi index 1cc65574..7349e92c 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -49,10 +49,6 @@ use to turn off IPv6 support, to disable the compilation of specific daemons, and to enable SNMP support. @table @option -@item --enable-guile -Turn on compilation of the zebra-guile interpreter. You will need the -guile library to make this. zebra-guile implementation is not yet -finished. So this option is only useful for zebra-guile developers. @item --disable-ipv6 Turn off IPv6 related features and daemons. Quagga configure script automatically detects IPv6 stack. But sometimes you might want to @@ -94,12 +90,16 @@ Enable support for Equal Cost Multipath. @var{ARG} is the maximum number of ECMP paths to allow, set to 0 to allow unlimited number of paths. @item --disable-rtadv Disable support IPV6 router advertisement in zebra. -@item --disable-tests -Do not build tests. Test programs are built by default, but not ran or -installed. They can be excluded from build with this option, which will -minimally decrease compile time and overhead. They can always be built and -executed at a later time by running @command{make check} in the @file{tests/} -subdirectory, even if they're excluded from build. +@item --enable-gcc-rdynamic +Pass the @command{-rdynamic} option to the linker driver. This is in most +cases neccessary for getting usable backtraces. This option defaults to on +if the compiler is detected as gcc, but giving an explicit enable/disable is +suggested. +@item --enable-backtrace +Controls backtrace support for the crash handlers. This is autodetected by +default. Using the switch will enforce the requested behaviour, failing with +an error if support is requested but not available. On BSD systems, this +needs libexecinfo, while on glibc support for this is part of libc itself. @end table You may specify any combination of the above options to the configure @@ -273,6 +273,7 @@ bgpd 2605/tcp # BGPd vty ospf6d 2606/tcp # OSPF6d vty ospfapi 2607/tcp # ospfapi isisd 2608/tcp # ISISd vty +pimd 2611/tcp # PIMd vty @end example If you use a FreeBSD newer than 2.2.8, the above entries are already diff --git a/doc/ipv6.texi b/doc/ipv6.texi index b6cc4376..2482c1c0 100644 --- a/doc/ipv6.texi +++ b/doc/ipv6.texi @@ -136,8 +136,8 @@ for the lowest preference possible. Default: 0 @end deffn -+@deffn {Interface Command} {ipv6 nd home-agent-lifetime <0-65520>} {} -+@deffnx {Interface Command} {no ipv6 nd home-agent-lifetime [<0-65520>]} {} +@deffn {Interface Command} {ipv6 nd home-agent-lifetime <0-65520>} {} +@deffnx {Interface Command} {no ipv6 nd home-agent-lifetime [<0-65520>]} {} The value to be placed in Home Agent Option, when Home Agent config flag is set, which indicates to hosts Home Agent Lifetime. The default value of 0 means to place the current Router Lifetime value. diff --git a/doc/main.texi b/doc/main.texi index a5759137..4c11d244 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -10,7 +10,9 @@ different routing protocols. * Invoking zebra:: Running the program * Interface Commands:: Commands for zebra interfaces * Static Route Commands:: Commands for adding static routes +* Multicast RIB Commands:: Commands for controlling MRIB behavior * zebra Route Filtering:: Commands for zebra route filtering +* zebra FIB push interface:: Interface to optional FPM component * zebra Terminal Mode Commands:: Commands for zebra's VTY @end menu @@ -184,6 +186,86 @@ and later). After setting @var{tableno} with this command, static routes defined after this are added to the specified table. @end deffn +@node Multicast RIB Commands +@section Multicast RIB Commands + +The Multicast RIB provides a separate table of unicast destinations which +is used for Multicast Reverse Path Forwarding decisions. It is used with +a multicast source's IP address, hence contains not multicast group +addresses but unicast addresses. + +This table is fully separate from the default unicast table. However, +RPF lookup can include the unicast table. + +WARNING: RPF lookup results are non-responsive in this version of Quagga, +i.e. multicast routing does not actively react to changes in underlying +unicast topology! + +@deffn Command {ip multicast rpf-lookup-mode @var{mode}} {} +@deffnx Command {no ip multicast rpf-lookup-mode [@var{mode}]} {} + +@var{mode} sets the method used to perform RPF lookups. Supported modes: + +@table @samp +@item urib-only +Performs the lookup on the Unicast RIB. The Multicast RIB is never used. +@item mrib-only +Performs the lookup on the Multicast RIB. The Unicast RIB is never used. +@item mrib-then-urib +Tries to perform the lookup on the Multicast RIB. If any route is found, +that route is used. Otherwise, the Unicast RIB is tried. +@item lower-distance +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the lower administrative distance is used; if they're equal, the +Multicast RIB takes precedence. +@item longer-prefix +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the longer prefix length is used; if they're equal, the +Multicast RIB takes precedence. +@end table + +The @code{mrib-then-urib} setting is the default behavior if nothing is +configured. If this is the desired behavior, it should be explicitly +configured to make the configuration immune against possible changes in +what the default behavior is. + +WARNING: Unreachable routes do not receive special treatment and do not +cause fallback to a second lookup. +@end deffn + +@deffn Command {show ip rpf @var{addr}} {} + +Performs a Multicast RPF lookup, as configured with +@command{ip multicast rpf-lookup-mode @var{mode}}. @var{addr} specifies +the multicast source address to look up. + +@example +> show ip rpf 192.0.2.1 +Routing entry for 192.0.2.0/24 using Unicast RIB + Known via "kernel", distance 0, metric 0, best + * 198.51.100.1, via eth0 +@end example + +Indicates that a multicast source lookup for 192.0.2.1 would use an +Unicast RIB entry for 192.0.2.0/24 with a gateway of 198.51.100.1. +@end deffn + +@deffn Command {show ip rpf} {} + +Prints the entire Multicast RIB. Note that this is independent of the +configured RPF lookup mode, the Multicast RIB may be printed yet not +used at all. +@end deffn + +@deffn Command {ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} +@deffnx Command {no ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} + +Adds a static route entry to the Multicast RIB. This performs exactly as +the @command{ip route} command, except that it inserts the route in the +Multicast RIB instead of the Unicast RIB. +@end deffn + + @node zebra Route Filtering @section zebra Route Filtering Zebra supports @command{prefix-list} and @command{route-map} to match @@ -227,6 +309,54 @@ ip protocol rip route-map RM1 @end group @end example +@node zebra FIB push interface +@section zebra FIB push interface + +Zebra supports a 'FIB push' interface that allows an external +component to learn the forwarding information computed by the Quagga +routing suite. + +In Quagga, the Routing Information Base (RIB) resides inside +zebra. Routing protocols communicate their best routes to zebra, and +zebra computes the best route across protocols for each prefix. This +latter information makes up the Forwarding Information Base +(FIB). Zebra feeds the FIB to the kernel, which allows the IP stack in +the kernel to forward packets according to the routes computed by +Quagga. The kernel FIB is updated in an OS-specific way. For example, +the @code{netlink} interface is used on Linux, and route sockets are +used on FreeBSD. + +The FIB push interface aims to provide a cross-platform mechanism to +support scenarios where the router has a forwarding path that is +distinct from the kernel, commonly a hardware-based fast path. In +these cases, the FIB needs to be maintained reliably in the fast path +as well. We refer to the component that programs the forwarding plane +(directly or indirectly) as the Forwarding Plane Manager or FPM. + +The FIB push interface comprises of a TCP connection between zebra and +the FPM. The connection is initiated by zebra -- that is, the FPM acts +as the TCP server. + +The relevant zebra code kicks in when zebra is configured with the +@code{--enable-fpm} flag. Zebra periodically attempts to connect to +the well-known FPM port. Once the connection is up, zebra starts +sending messages containing routes over the socket to the FPM. Zebra +sends a complete copy of the forwarding table to the FPM, including +routes that it may have picked up from the kernel. The existing +interaction of zebra with the kernel remains unchanged -- that is, the +kernel continues to receive FIB updates as before. + +The format of the messages exchanged with the FPM is defined by the +file @file{fpm/fpm.h} in the quagga tree. + +The zebra FPM interface uses replace semantics. That is, if a 'route +add' message for a prefix is followed by another 'route add' message, +the information in the second message is complete by itself, and +replaces the information sent in the first message. + +If the connection to the FPM goes down for some reason, zebra sends +the FPM a complete copy of the forwarding table(s) when it reconnects. + @node zebra Terminal Mode Commands @section zebra Terminal Mode Commands @@ -271,3 +401,13 @@ If so, the box can't work as a router. @deffn Command {show ipv6forward} {} Display whether the host's IP v6 forwarding is enabled or not. @end deffn + +@deffn Command {show zebra fpm stats} {} +Display statistics related to the zebra code that interacts with the +optional Forwarding Plane Manager (FPM) component. +@end deffn + +@deffn Command {clear zebra fpm stats} {} +Reset statistics related to the zebra code that interacts with the +optional Forwarding Plane Manager (FPM) component. +@end deffn diff --git a/doc/mpls/opaque_lsa.txt b/doc/mpls/opaque_lsa.txt index 7d5c7fed..ae89ee34 100644 --- a/doc/mpls/opaque_lsa.txt +++ b/doc/mpls/opaque_lsa.txt @@ -20,7 +20,7 @@ | (Callback functions) | +----------------------+ - + 2. Self-originated Opaque-LSAs per LSA-type. 2.1 Type-11 (AS-external) Opaque-LSAs @@ -110,7 +110,7 @@ : | +-------------+ : : --- : :..........................................................................: - + 2.2 Type-10 (area-local) Opaque-LSAs struct @@ -204,7 +204,7 @@ : | +-------------+ : : --- : :..........................................................................: - + 2.3 Type-9 (link-local) Opaque-LSAs struct @@ -300,7 +300,7 @@ : +-------------+ : :..........................................................................: - + 3. Internal structures for MPLS-TE parameter management. struct diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index 6667221c..31f4db0c 100644 --- a/doc/ospf6d.texi +++ b/doc/ospf6d.texi @@ -28,6 +28,56 @@ Bind interface to specified area, and start sending OSPF packets. @var{area} ca be specified as 0. @end deffn +@deffn {OSPF6 Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {} +@deffnx {OSPF6 Command} {no timers throttle spf} {} +This command sets the initial @var{delay}, the @var{initial-holdtime} +and the @var{maximum-holdtime} between when SPF is calculated and the +event which triggered the calculation. The times are specified in +milliseconds and must be in the range of 0 to 600000 milliseconds. + +The @var{delay} specifies the minimum amount of time to delay SPF +calculation (hence it affects how long SPF calculation is delayed after +an event which occurs outside of the holdtime of any previous SPF +calculation, and also serves as a minimum holdtime). + +Consecutive SPF calculations will always be seperated by at least +'hold-time' milliseconds. The hold-time is adaptive and initially is +set to the @var{initial-holdtime} configured with the above command. +Events which occur within the holdtime of the previous SPF calculation +will cause the holdtime to be increased by @var{initial-holdtime}, bounded +by the @var{maximum-holdtime} configured with this command. If the adaptive +hold-time elapses without any SPF-triggering event occuring then +the current holdtime is reset to the @var{initial-holdtime}. + +@example +@group +router ospf6 + timers throttle spf 200 400 10000 +@end group +@end example + +In this example, the @var{delay} is set to 200ms, the @var{initial +holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence +there will always be at least 200ms between an event which requires SPF +calculation and the actual SPF calculation. Further consecutive SPF +calculations will always be seperated by between 400ms to 10s, the +hold-time increasing by 400ms each time an SPF-triggering event occurs +within the hold-time of the previous SPF calculation. + +@end deffn + +@deffn {OSPF6 Command} {auto-cost reference-bandwidth @var{cost}} {} +@deffnx {OSPF6 Command} {no auto-cost reference-bandwidth} {} +This sets the reference bandwidth for cost calculations, where this +bandwidth is considered equivalent to an OSPF cost of 1, specified in +Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth 100Mbit/s +or higher will have a cost of 1. Cost of lower bandwidth links will be +scaled with reference to this cost). + +This configuration setting MUST be consistent across all routers +within the OSPF domain. +@end deffn + @node OSPF6 area @section OSPF6 area @@ -37,7 +87,8 @@ Area support for OSPFv3 is not yet implemented. @section OSPF6 interface @deffn {Interface Command} {ipv6 ospf6 cost COST} {} -Sets interface's output cost. Default value is 1. +Sets interface's output cost. Default value depends on the interface +bandwidth and on the auto-cost reference bandwidth. @end deffn @deffn {Interface Command} {ipv6 ospf6 hello-interval HELLOINTERVAL} {} @@ -60,6 +111,10 @@ Sets interface's Router Priority. Default value is 1. Sets interface's Inf-Trans-Delay. Default value is 1. @end deffn +@deffn {Interface Command} {ipv6 ospf6 network (broadcast|point-to-point)} {} +Set explicitly network type for specifed interface. +@end deffn + @node Redistribute routes to OSPF6 @section Redistribute routes to OSPF6 diff --git a/doc/overview.texi b/doc/overview.texi index 7acfc0e9..2301c4b7 100644 --- a/doc/overview.texi +++ b/doc/overview.texi @@ -4,7 +4,7 @@ @uref{http://www.quagga.net,,Quagga} is a routing software package that provides TCP/IP based routing services with routing protocols support such -as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, BGP-4, and BGP-4+ (@pxref{Supported +as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, IS-IS, BGP-4, and BGP-4+ (@pxref{Supported RFCs}). Quagga also supports special BGP Route Reflector and Route Server behavior. In addition to traditional IPv4 routing protocols, Quagga also supports IPv6 routing protocols. With SNMP daemon which supports SMUX and AgentX @@ -52,7 +52,7 @@ software is very easy. The only thing you have to do is to set up the interfaces and put a few commands about static routes and/or default routes. If the network is rather large, or if the network structure changes frequently, you will want to take advantage of Quagga's dynamic routing -protocol support for protocols such as RIP, OSPF or BGP. +protocol support for protocols such as RIP, OSPF, IS-IS or BGP. Traditionally, UNIX based router configuration is done by @command{ifconfig} and @command{route} commands. Status of routing @@ -63,12 +63,13 @@ mode, the other is enable mode. Normal mode user can only view system status, enable mode user can change system configuration. This UNIX account independent feature will be great help to the router administrator. - Currently, Quagga supports common unicast routing protocols. Multicast -routing protocols such as BGMP, PIM-SM, PIM-DM may be supported in Quagga -2.0. MPLS support is going on. In the future, TCP/IP filtering control, -QoS control, diffserv configuration will be added to Quagga. Quagga -project's final goal is making a productive, quality, free TCP/IP routing -software. + Currently, Quagga supports common unicast routing protocols, that is BGP, +OSPF, RIP and IS-IS. Upcoming for MPLS support, an implementation of LDP is +currently being prepared for merging. Implementations of BFD and PIM-SSM +(IPv4) also exist, but are not actively being worked on. + + The ultimate goal of the Quagga project is making a productive, quality, free +TCP/IP routing software package. @node System Architecture @comment node-name, next, previous, up @@ -139,7 +140,7 @@ events. @cindex Compatibility with other systems @cindex Operating systems that support Quagga -Currently Quagga supports @sc{gnu}/Linux, BSD and Solaris. Porting Quagga +Currently Quagga supports @sc{gnu}/Linux and BSD. Porting Quagga to other platforms is not too difficult as platform dependent code should most be limited to the @command{zebra} daemon. Protocol daemons are mostly platform independent. Please let us know when you find out Quagga runs on a @@ -152,15 +153,40 @@ functionality on further platforms. @sp 1 @itemize @bullet @item -@sc{gnu}/Linux 2.4.x and higher +@sc{gnu}/Linux +@item +FreeBSD @item -FreeBSD 4.x and higher +NetBSD +@item +OpenBSD +@end itemize + +Versions of these platforms that are older than around 2 years from the point +of their original release (in case of @sc{gnu}/Linux, this is since the kernel's +release on kernel.org) may need some work. Similarly, the following platforms +may work with some effort: + +@sp 1 +@itemize @bullet +@item +Solaris +@item +Mac OSX +@end itemize + +Also note that, in particular regarding proprietary platforms, compiler +and C library choice will affect Quagga. Only recent versions of the +following C compilers are well-tested: + +@sp 1 +@itemize @bullet @item -NetBSD 1.6 and higher +@sc{gnu}'s GCC @item -OpenBSD 2.5 and higher +LLVM's clang @item -Solaris 8 and higher +Intel's ICC @end itemize @node Supported RFCs diff --git a/doc/pimd.8 b/doc/pimd.8 new file mode 100644 index 00000000..0dd170a2 --- /dev/null +++ b/doc/pimd.8 @@ -0,0 +1,125 @@ +.TH PIM 8 "10 December 2008" "Quagga PIM daemon" "Version 0.99.11" +.SH NAME +pimd \- a PIM routing for use with Quagga Routing Suite. +.SH SYNOPSIS +.B pimd +[ +.B \-dhvZ +] [ +.B \-f +.I config-file +] [ +.B \-i +.I pid-file +] [ +.B \-z +.I path +] [ +.B \-P +.I port-number +] [ +.B \-A +.I vty-address +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] +.SH DESCRIPTION +.B pimd +is a protocol-independent multicast component that works with the +.B Quagga +Routing Suite. +.SH OPTIONS +Options available for the +.B pimd +command: +.TP +\fB\-d\fR, \fB\-\-daemon\fR +Runs in daemon mode, forking and exiting from tty. +.TP +\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR +Specifies the config file to use for startup. If not specified this +option will likely default to \fB\fI/usr/local/etc/pimd.conf\fR. +.TP +\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR +Specify the group to run as. Default is \fIquagga\fR. +.TP +\fB\-h\fR, \fB\-\-help\fR +A brief message. +.TP +\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR +When pimd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart pimd. The likely default is \fB\fI/var/run/pimd.pid\fR. +.TP +\fB\-z\fR, \fB\-\-socket \fR\fIpath\fR +Specify the socket path for contacting the zebra daemon. +The likely default is \fB\fI/var/run/zserv.api\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the pimd VTY will listen on. This defaults to +2611, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the pimd VTY will listen on. Default is all +interfaces. +.TP +\fB\-u\fR, \fB\-\-user \fR\fIuser\fR +Specify the user to run as. Default is \fIquagga\fR. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.TP +\fB\-Z\fR, \fB\-\-debug_zclient\fR +Enable logging information for zclient debugging. +.SH FILES +.TP +.BI /usr/local/sbin/pimd +The default location of the +.B pimd +binary. +.TP +.BI /usr/local/etc/pimd.conf +The default location of the +.B pimd +config file. +.TP +.BI /var/run/pimd.pid +The default location of the +.B pimd +pid file. +.TP +.BI /var/run/zserv.api +The default location of the +.B zebra +unix socket file. +.TP +.BI $(PWD)/pimd.log +If the +.B pimd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBpimd\fR. +.SH WARNING +This man page is intended to be a quick reference for command line +options. +.SH DIAGNOSTICS +The pimd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. +.SH "SEE ALSO" +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +\fBpimd\fR is in early development at the moment and is not ready for +production use. + +.B pimd +eats bugs for breakfast. If you have food for the maintainers try +.BI https://github.com/udhos/qpimd +.SH AUTHORS +See +.BI https://github.com/udhos/qpimd +for an accurate list of authors. + diff --git a/doc/quagga.texi b/doc/quagga.texi index ff913aa5..83650710 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -1,12 +1,11 @@ \input texinfo @c -*- texinfo -*- + @c %**start of header -@setchapternewpage odd -@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @setfilename quagga.info -@c %**end of header - @c Set variables - sourced from defines.texi @include defines.texi +@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} +@c %**end of header @c automake will automatically generate version.texi @c and set EDITION, VERSION, UPDATED and UPDATED-MONTH diff --git a/doc/ripd.texi b/doc/ripd.texi index c6f804af..78d63eed 100644 --- a/doc/ripd.texi +++ b/doc/ripd.texi @@ -560,14 +560,14 @@ for routes redistributed into RIP. @c Exmaple here. -@deffn Command {show ip protocols} {} +@deffn Command {show ip rip status} {} The command displays current RIP status. It includes RIP timer, filtering, version, RIP enabled interface and RIP peer inforation. @end deffn @example @group -ripd> @b{show ip protocols} +ripd> @b{show ip rip status} Routing Protocol is "rip" Sending updates every 30 seconds with +/-50%, next due in 35 seconds Timeout after 180 seconds, garbage collect after 120 seconds diff --git a/doc/zebra.8 b/doc/zebra.8 index 23703e72..a40909a6 100644 --- a/doc/zebra.8 +++ b/doc/zebra.8 @@ -80,7 +80,7 @@ handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble. Solution is to increase receive buffer of netlink socket. Note that kernel -doesn't allow to increase it over maximum value defined in +< 2.6.14 doesn't allow to increase it over maximum value defined in \fI/proc/sys/net/core/rmem_max\fR. If you want to do it, you have to increase maximum before starting zebra. diff --git a/fpm/fpm.h b/fpm/fpm.h new file mode 100644 index 00000000..96f05f48 --- /dev/null +++ b/fpm/fpm.h @@ -0,0 +1,273 @@ +/* + * Public definitions pertaining to the Forwarding Plane Manager component. + * + * Permission is granted to use, copy, modify and/or distribute this + * software under either one of the licenses below. + * + * Note that if you use other files from the Quagga tree directly or + * indirectly, then the licenses in those files still apply. + * + * Please retain both licenses below when modifying this code in the + * Quagga tree. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + */ + +/* + * License Option 1: GPL + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * License Option 2: ISC License + * + * 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 _FPM_H +#define _FPM_H + +/* + * The Forwarding Plane Manager (FPM) is an optional component that + * may be used in scenarios where the router has a forwarding path + * that is distinct from the kernel, commonly a hardware-based fast + * path. It is responsible for programming forwarding information + * (such as routes and nexthops) in the fast path. + * + * In Quagga, the Routing Information Base is maintained in the + * 'zebra' infrastructure daemon. Routing protocols communicate their + * best routes to zebra, and zebra computes the best route across + * protocols for each prefix. This latter information comprises the + * bulk of the Forwarding Information Base. + * + * This header file defines a point-to-point interface using which + * zebra can update the FPM about changes in routes. The communication + * takes place over a stream socket. The FPM listens on a well-known + * TCP port, and zebra initiates the connection. + * + * All messages sent over the connection start with a short FPM + * header, fpm_msg_hdr_t. In the case of route add/delete messages, + * the header is followed by a netlink message. Zebra should send a + * complete copy of the forwarding table(s) to the FPM, including + * routes that it may have picked up from the kernel. + * + * The FPM interface uses replace semantics. That is, if a 'route add' + * message for a prefix is followed by another 'route add' message, the + * information in the second message is complete by itself, and replaces + * the information sent in the first message. + * + * If the connection to the FPM goes down for some reason, the client + * (zebra) should send the FPM a complete copy of the forwarding + * table(s) when it reconnects. + */ + +#define FPM_DEFAULT_PORT 2620 + +/* + * Largest message that can be sent to or received from the FPM. + */ +#define FPM_MAX_MSG_LEN 4096 + +/* + * Header that precedes each fpm message to/from the FPM. + */ +typedef struct fpm_msg_hdr_t_ +{ + /* + * Protocol version. + */ + uint8_t version; + + /* + * Type of message, see below. + */ + uint8_t msg_type; + + /* + * Length of entire message, including the header, in network byte + * order. + * + * Note that msg_len is rounded up to make sure that message is at + * the desired alignment. This means that some payloads may need + * padding at the end. + */ + uint16_t msg_len; +} fpm_msg_hdr_t; + +/* + * The current version of the FPM protocol is 1. + */ +#define FPM_PROTO_VERSION 1 + +typedef enum fpm_msg_type_e_ { + FPM_MSG_TYPE_NONE = 0, + + /* + * Indicates that the payload is a completely formed netlink + * message. + */ + FPM_MSG_TYPE_NETLINK = 1, +} fpm_msg_type_e; + +/* + * The FPM message header is aligned to the same boundary as netlink + * messages (4). This means that a netlink message does not need + * padding when encapsulated in an FPM message. + */ +#define FPM_MSG_ALIGNTO 4 + +/* + * fpm_msg_align + * + * Round up the given length to the desired alignment. + */ +static inline size_t +fpm_msg_align (size_t len) +{ + return (len + FPM_MSG_ALIGNTO - 1) & ~(FPM_MSG_ALIGNTO - 1); +} + +/* + * The (rounded up) size of the FPM message header. This ensures that + * the message payload always starts at an aligned address. + */ +#define FPM_MSG_HDR_LEN (fpm_msg_align (sizeof (fpm_msg_hdr_t))) + +/* + * fpm_data_len_to_msg_len + * + * The length value that should be placed in the msg_len field of the + * header for a *payload* of size 'data_len'. + */ +static inline size_t +fpm_data_len_to_msg_len (size_t data_len) +{ + return fpm_msg_align (data_len) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_data + * + * Pointer to the payload of the given fpm header. + */ +static inline void * +fpm_msg_data (fpm_msg_hdr_t *hdr) +{ + return ((char*) hdr) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_len + */ +static inline size_t +fpm_msg_len (const fpm_msg_hdr_t *hdr) +{ + return ntohs (hdr->msg_len); +} + +/* + * fpm_msg_data_len + */ +static inline size_t +fpm_msg_data_len (const fpm_msg_hdr_t *hdr) +{ + return (fpm_msg_len (hdr) - FPM_MSG_HDR_LEN); +} + +/* + * fpm_msg_next + * + * Move to the next message in a buffer. + */ +static inline fpm_msg_hdr_t * +fpm_msg_next (fpm_msg_hdr_t *hdr, size_t *len) +{ + size_t msg_len; + + msg_len = fpm_msg_len (hdr); + + if (len) { + if (*len < msg_len) + { + assert(0); + return NULL; + } + *len -= msg_len; + } + + return (fpm_msg_hdr_t *) (((char*) hdr) + msg_len); +} + +/* + * fpm_msg_hdr_ok + * + * Returns TRUE if a message header looks well-formed. + */ +static inline int +fpm_msg_hdr_ok (const fpm_msg_hdr_t *hdr) +{ + size_t msg_len; + + if (hdr->msg_type == FPM_MSG_TYPE_NONE) + return 0; + + msg_len = fpm_msg_len (hdr); + + if (msg_len < FPM_MSG_HDR_LEN || msg_len > FPM_MAX_MSG_LEN) + return 0; + + if (fpm_msg_align (msg_len) != msg_len) + return 0; + + return 1; +} + +/* + * fpm_msg_ok + * + * Returns TRUE if a message looks well-formed. + * + * @param len The length in bytes from 'hdr' to the end of the buffer. + */ +static inline int +fpm_msg_ok (const fpm_msg_hdr_t *hdr, size_t len) +{ + if (len < FPM_MSG_HDR_LEN) + return 0; + + if (!fpm_msg_hdr_ok (hdr)) + return 0; + + if (fpm_msg_len (hdr) > len) + return 0; + + return 1; +} + +#endif /* _FPM_H */ diff --git a/guile/.gitignore b/guile/.gitignore deleted file mode 100644 index 5c2e06b6..00000000 --- a/guile/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -Makefile -*.o -zebra-guile -Makefile.in -.nfs* -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/guile/Makefile.am b/guile/Makefile.am deleted file mode 100644 index 8d7008e9..00000000 --- a/guile/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -## Process this file with Automake to create Makefile.in - -INCLUDES = @GUILE_CFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DEFS = @DEFS@ -I. -I$(srcdir) - -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) - -bin_PROGRAMS = zebra-guile -zebra_guile_SOURCES = zebra-guile.c zebra-support.c guile-bgp.c -noinst_HEADERS = zebra-guile.h -zebra_guile_LDADD = @GUILE_LDFLAGS@ ../bgpd/libbgp.a ../lib/libzebra.la diff --git a/guile/Makefile.in b/guile/Makefile.in deleted file mode 100644 index 2773029b..00000000 --- a/guile/Makefile.in +++ /dev/null @@ -1,299 +0,0 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am - -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - - -SHELL = @SHELL@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -sbindir = @sbindir@ -libexecdir = @libexecdir@ -datadir = @datadir@ -sysconfdir = @sysconfdir@ -sharedstatedir = @sharedstatedir@ -localstatedir = @localstatedir@ -libdir = @libdir@ -infodir = @infodir@ -mandir = @mandir@ -includedir = @includedir@ -oldincludedir = /usr/include - -DESTDIR = - -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ - -top_builddir = .. - -ACLOCAL = @ACLOCAL@ -AUTOCONF = @AUTOCONF@ -AUTOMAKE = @AUTOMAKE@ -AUTOHEADER = @AUTOHEADER@ - -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -transform = @program_transform_name@ - -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -host_alias = @host_alias@ -host_triplet = @host@ -AR = @AR@ -BGPD = @BGPD@ -CC = @CC@ -CPP = @CPP@ -CURSES = @CURSES@ -IF_METHOD = @IF_METHOD@ -IF_PROC = @IF_PROC@ -IPFORWARD = @IPFORWARD@ -KERNEL_METHOD = @KERNEL_METHOD@ -LIBPAM = @LIBPAM@ -LIB_IPV6 = @LIB_IPV6@ -LIB_REGEX = @LIB_REGEX@ -MAKEINFO = @MAKEINFO@ -MULTIPATH_NUM = @MULTIPATH_NUM@ -OSPF6D = @OSPF6D@ -OSPFD = @OSPFD@ -OTHER_METHOD = @OTHER_METHOD@ -PACKAGE = @PACKAGE@ -RANLIB = @RANLIB@ -RIPD = @RIPD@ -RIPNGD = @RIPNGD@ -RTREAD_METHOD = @RTREAD_METHOD@ -RT_METHOD = @RT_METHOD@ -VERSION = @VERSION@ -VTYSH = @VTYSH@ -ZEBRA = @ZEBRA@ - -INCLUDES = @GUILE_CFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -DEFS = @DEFS@ -I. -I$(srcdir) - -bin_PROGRAMS = zebra-guile -zebra_guile_SOURCES = zebra-guile.c zebra-support.c guile-bgp.c -noinst_HEADERS = zebra-guile.h -zebra_guile_LDADD = @GUILE_LDFLAGS@ ../bgpd/libbgp.a ../lib/libzebra.a -mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs -CONFIG_HEADER = ../config.h -CONFIG_CLEAN_FILES = -PROGRAMS = $(bin_PROGRAMS) - -CPPFLAGS = @CPPFLAGS@ -LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ -zebra_guile_OBJECTS = zebra-guile.o zebra-support.o guile-bgp.o -zebra_guile_DEPENDENCIES = ../bgpd/libbgp.a ../lib/libzebra.a -zebra_guile_LDFLAGS = -CFLAGS = @CFLAGS@ -COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -CCLD = $(CC) -LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ -HEADERS = $(noinst_HEADERS) - -DIST_COMMON = README ChangeLog Makefile.am Makefile.in - - -DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) - -TAR = tar -GZIP_ENV = --best -SOURCES = $(zebra_guile_SOURCES) -OBJECTS = $(zebra_guile_OBJECTS) - -all: all-redirect -.SUFFIXES: -.SUFFIXES: .S .c .o .s -$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) - cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps guile/Makefile - -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - cd $(top_builddir) \ - && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status - - -mostlyclean-binPROGRAMS: - -clean-binPROGRAMS: - -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) - -distclean-binPROGRAMS: - -maintainer-clean-binPROGRAMS: - -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - $(mkinstalldirs) $(DESTDIR)$(bindir) - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - if test -f $$p; then \ - echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ - $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ - else :; fi; \ - done - -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - list='$(bin_PROGRAMS)'; for p in $$list; do \ - rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ - done - -.c.o: - $(COMPILE) -c $< - -.s.o: - $(COMPILE) -c $< - -.S.o: - $(COMPILE) -c $< - -mostlyclean-compile: - -rm -f *.o core *.core - -clean-compile: - -distclean-compile: - -rm -f *.tab.c - -maintainer-clean-compile: - -zebra-guile: $(zebra_guile_OBJECTS) $(zebra_guile_DEPENDENCIES) - @rm -f zebra-guile - $(LINK) $(zebra_guile_LDFLAGS) $(zebra_guile_OBJECTS) $(zebra_guile_LDADD) $(LIBS) - -tags: TAGS - -ID: $(HEADERS) $(SOURCES) $(LISP) - list='$(SOURCES) $(HEADERS)'; \ - unique=`for i in $$list; do echo $$i; done | \ - awk ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - here=`pwd` && cd $(srcdir) \ - && mkid -f$$here/ID $$unique $(LISP) - -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS)'; \ - unique=`for i in $$list; do echo $$i; done | \ - awk ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ - || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) - -mostlyclean-tags: - -clean-tags: - -distclean-tags: - -rm -f TAGS ID - -maintainer-clean-tags: - -distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) - -subdir = guile - -distdir: $(DISTFILES) - @for file in $(DISTFILES); do \ - d=$(srcdir); \ - if test -d $$d/$$file; then \ - cp -pr $$/$$file $(distdir)/$$file; \ - else \ - test -f $(distdir)/$$file \ - || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ - || cp -p $$d/$$file $(distdir)/$$file || :; \ - fi; \ - done -info-am: -info: info-am -dvi-am: -dvi: dvi-am -check-am: all-am -check: check-am -installcheck-am: -installcheck: installcheck-am -install-exec-am: install-binPROGRAMS -install-exec: install-exec-am - -install-data-am: -install-data: install-data-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -install: install-am -uninstall-am: uninstall-binPROGRAMS -uninstall: uninstall-am -all-am: Makefile $(PROGRAMS) $(HEADERS) -all-redirect: all-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install -installdirs: - $(mkinstalldirs) $(DESTDIR)$(bindir) - - -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -rm -f Makefile $(CONFIG_CLEAN_FILES) - -rm -f config.cache config.log stamp-h stamp-h[0-9]* - -maintainer-clean-generic: -mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \ - mostlyclean-tags mostlyclean-generic - -mostlyclean: mostlyclean-am - -clean-am: clean-binPROGRAMS clean-compile clean-tags clean-generic \ - mostlyclean-am - -clean: clean-am - -distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \ - distclean-generic clean-am - -distclean: distclean-am - -maintainer-clean-am: maintainer-clean-binPROGRAMS \ - maintainer-clean-compile maintainer-clean-tags \ - maintainer-clean-generic distclean-am - @echo "This command is intended for maintainers to use;" - @echo "it deletes files that may require special tools to rebuild." - -maintainer-clean: maintainer-clean-am - -.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ -maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ -mostlyclean-compile distclean-compile clean-compile \ -maintainer-clean-compile tags mostlyclean-tags distclean-tags \ -clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ -check-am installcheck-am installcheck install-exec-am install-exec \ -install-data-am install-data install-am install uninstall-am uninstall \ -all-redirect all-am all installdirs mostlyclean-generic \ -distclean-generic clean-generic maintainer-clean-generic clean \ -mostlyclean distclean maintainer-clean - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/guile/README b/guile/README deleted file mode 100644 index 8e18fae5..00000000 --- a/guile/README +++ /dev/null @@ -1,17 +0,0 @@ - - zebra-guile - - Kunihiro Ishiguro - 1999 - -1. What is zebra-guile - -zebra-guile is GNU Zebra which linked with guile. Almost zebra's -command can be called from guile interpreter. So you can use guile as -a routing scripting language. - -2. How to use it. - -(define bgp (router-bgp 7675)) - -3. diff --git a/guile/guile-bgp.c b/guile/guile-bgp.c deleted file mode 100644 index fbd01ba0..00000000 --- a/guile/guile-bgp.c +++ /dev/null @@ -1,117 +0,0 @@ -/* Guile bgp interface. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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. - -GNU Zebra 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 General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -#include -#include - -#include "log.h" -#include "bgpd/bgpd.h" - -/* static SCM scm_mark_bgp (SCM obj); */ -static size_t scm_free_bgp (SCM vect); -static int scm_print_bgp (SCM vect, SCM port, scm_print_state *pstate); -static SCM scm_equalp_bgp (SCM a, SCM b); - -/* Tag of scheme type of bgp. */ -long scm_tag_bgp; - -static scm_smobfuns bgp_funs = -{ - scm_mark0, scm_free_bgp, scm_print_bgp, scm_equalp_bgp -}; - -static int -scm_print_bgp (SCM vect, SCM port, scm_print_state *pstate) -{ - unsigned short num; - struct bgp *bgp; - - num = 0; - bgp = (struct bgp *) SCM_CDR (vect); - num = bgp->as; - scm_puts ("#', port); - return 1; -} - -static size_t -scm_free_bgp (SCM obj) -{ - /* dummy function. */ - return 10; -} - -static SCM -scm_equalp_bgp (SCM a, SCM b) -{ - - return SCM_BOOL_F; -} - -/* Make bgp instance. */ -SCM -scm_router_bgp (SCM as_number) -{ - SCM cell; - long num; - struct bgp *bgp; - struct bgp *bgp_create (); - - SCM_ASSERT (SCM_INUMP (as_number), as_number, SCM_ARG1, "router-bgp"); - - SCM_DEFER_INTS; - - num = gh_scm2long (as_number); - - /* Make new bgp object. */ - bgp = bgp_create (); - bgp->as = num; - - SCM_NEWCELL (cell); - SCM_SETCAR (cell, scm_tag_bgp); - SCM_SETCDR (cell, bgp); - - SCM_ALLOW_INTS; - - return cell; -} - -#if 0 -SCM -scm_router_bgp_list () -{ - return NULL; -} -#endif - -void -init_bgp () -{ - void bgp_init (); - - bgp_init (); - - /* Initi types. */ - scm_tag_bgp = scm_newsmob (&bgp_funs); - - gh_new_procedure ("router-bgp", scm_router_bgp, 1, 0, 0); - /* gh_new_procedure ("router-bgp-list", scm_router_bgp_list, 0, 0, 0); */ -} diff --git a/guile/zebra-guile.c b/guile/zebra-guile.c deleted file mode 100644 index f618dbc5..00000000 --- a/guile/zebra-guile.c +++ /dev/null @@ -1,71 +0,0 @@ -/* Zebra guile interface. - Copyright (C) 1998, 99 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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. - -GNU Zebra 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 General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -#include -#include "zebra-guile.h" - -#include "zebra.h" -#include "thread.h" - -struct thread *master; - -static void -init_libzebra () -{ - void cmd_init(); - void vty_init(); - void memory_init(); - - cmd_init (1); - vty_init (); - memory_init (); -} - -/* Install scheme procudures. */ -void -init_zebra_guile () -{ - init_libzebra (); - - init_bgp (); - -#if 0 - init_zebra (); - init_rip (); - init_ospf (); -#endif /* 0 */ -} - -static void -inner_main (void *closure, int argc, char **argv) -{ - /* Install zebra related scheme procedures. */ - init_zebra_guile (); - - /* Invoke interpreter. */ - scm_shell (argc, argv); -} - -int -main (int argc, char **argv) -{ - scm_boot_guile (argc, argv, inner_main, 0); - return 0; /* Not reached */ -} diff --git a/guile/zebra-guile.h b/guile/zebra-guile.h deleted file mode 100644 index f43e287d..00000000 --- a/guile/zebra-guile.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Zebra guile header. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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. - -GNU Zebra 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 General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -void init_bgp (); diff --git a/guile/zebra-support.c b/guile/zebra-support.c deleted file mode 100644 index 9a6ef814..00000000 --- a/guile/zebra-support.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Zebra guile interface support. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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. - -GNU Zebra 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 General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ diff --git a/isisd/Makefile.am b/isisd/Makefile.am index 4e9b2441..dba681b1 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ @ISIS_TOPOLOGY_INCLUDES@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h index 1a80aec8..42b9bc88 100644 --- a/isisd/include-netbsd/iso.h +++ b/isisd/include-netbsd/iso.h @@ -192,7 +192,7 @@ extern struct protosw isosw[]; #else /* user utilities definitions from the iso library */ -#ifdef SUNOS_5 +#ifndef HAVE_SYS_CDEFS_H #define __P(x) x #define __BEGIN_DECLS #define __END_DECLS diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 468b0a69..414885fc 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -207,7 +207,7 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s", adj_name, - adj->circuit ? adj->circuit->interface->name : "no circuit", + adj->circuit->interface->name, adj_state2string (old_state), adj_state2string (new_state), reason ? reason : "unspecified"); @@ -427,7 +427,7 @@ isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail) vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids)); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, " SNPA: %s", snpa_print (adj->snpa)); - if (adj->circuit->circ_type == CIRCUIT_T_BROADCAST) + if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id (adj->lanid); if (dyn) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index c09c3a28..3d9fb473 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -327,6 +327,8 @@ isis_circuit_del_addr (struct isis_circuit *circuit, zlog_warn ("Nonexitant ip address %s removal attempt from \ circuit %d", buf, circuit->circuit_id); } + + prefix_ipv4_free (ipv4); } #ifdef HAVE_IPV6 if (connected->address->family == AF_INET6) @@ -370,6 +372,8 @@ isis_circuit_del_addr (struct isis_circuit *circuit, } else if (circuit->area) lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + + prefix_ipv6_free (ipv6); } #endif /* HAVE_IPV6 */ return; diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 5d74a71b..a58ba490 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -101,6 +101,7 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) break; case ISIS_DISABLE: zlog_warn ("circuit already disabled"); + break; case IF_DOWN_FROM_Z: zlog_warn ("circuit already disconnected"); break; diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index 73b6d3e7..0a82718d 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -442,13 +442,9 @@ open_dlpi_dev (struct isis_circuit *circuit) * 8.4.2 - Broadcast subnetwork IIH PDUs */ retval = 0; - if (circuit->is_type & IS_LEVEL_1) - { - retval |= dlpimcast (fd, ALL_L1_ISS); - retval |= dlpimcast (fd, ALL_ISS); - } - if (circuit->is_type & IS_LEVEL_2) - retval |= dlpimcast (fd, ALL_L2_ISS); + retval |= dlpimcast (fd, ALL_L1_ISS); + retval |= dlpimcast (fd, ALL_ISS); + retval |= dlpimcast (fd, ALL_L2_ISS); if (retval != 0) { diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 3887b7c5..96d5762d 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -280,26 +280,29 @@ isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype) return; } - switch (circuit->is_type) + if (! circuit->is_passive) { - case IS_LEVEL_1: - if (newtype == IS_LEVEL_2) - circuit_resign_level (circuit, 1); - circuit_commence_level (circuit, 2); - break; - case IS_LEVEL_1_AND_2: - if (newtype == IS_LEVEL_1) - circuit_resign_level (circuit, 2); - else - circuit_resign_level (circuit, 1); - break; - case IS_LEVEL_2: - if (newtype == IS_LEVEL_1) - circuit_resign_level (circuit, 2); - circuit_commence_level (circuit, 1); - break; - default: - break; + switch (circuit->is_type) + { + case IS_LEVEL_1: + if (newtype == IS_LEVEL_2) + circuit_resign_level (circuit, 1); + circuit_commence_level (circuit, 2); + break; + case IS_LEVEL_1_AND_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + else + circuit_resign_level (circuit, 1); + break; + case IS_LEVEL_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + circuit_commence_level (circuit, 1); + break; + default: + break; + } } circuit->is_type = newtype; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 082e9dcd..f2a7923d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1631,7 +1631,7 @@ lsp_generate (struct isis_area *area, int level) static int lsp_regenerate (struct isis_area *area, int level) { - dict_t *lspdb = area->lspdb[level - 1]; + dict_t *lspdb; struct isis_lsp *lsp, *frag; struct listnode *node; u_char lspid[ISIS_SYS_ID_LEN + 2]; @@ -1640,6 +1640,8 @@ lsp_regenerate (struct isis_area *area, int level) if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; + lspdb = area->lspdb[level - 1]; + memset (lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index dcbc3de9..283b7eaa 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -144,7 +144,7 @@ Daemon which manages IS-IS routing\n\n\ -C, --dryrun Check configuration for validity and exit\n\ -h, --help Display this help and exit\n\ \n\ -Report bugs to http://bugzilla.quagga.net\n", progname); +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); @@ -339,8 +339,6 @@ main (int argc, char **argv, char **envp) isis_zebra_init (); - sort_node (); - /* parse config file */ /* this is needed three times! because we have interfaces before the areas */ vty_read_config (config_file, config_default); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index bfa1e4e9..166dd7c0 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -62,7 +62,7 @@ #endif /* PNBBY */ /* Utility mask array. */ -static u_char maskbit[] = { +static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; @@ -402,6 +402,7 @@ process_p2p_hello (struct isis_circuit *circuit) u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; uint16_t pdu_len; struct tlvs tlvs; + int v4_usable = 0, v6_usable = 0; if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -493,6 +494,13 @@ process_p2p_hello (struct isis_circuit *circuit) return ISIS_WARNING; } + if (!(found & TLVFLAG_NLPID)) + { + zlog_warn ("No supported protocols TLV in P2P IS to IS hello"); + free_tlvs (&tlvs); + return ISIS_WARNING; + } + /* 8.2.5.1 c) Authentication */ if (circuit->passwd.type) { @@ -511,11 +519,44 @@ process_p2p_hello (struct isis_circuit *circuit) /* * check if it's own interface ip match iih ip addrs */ - if ((found & TLVFLAG_IPV4_ADDR) == 0 || - ip_match (circuit->ip_addrs, tlvs.ipv4_addrs) == 0) + if (found & TLVFLAG_IPV4_ADDR) + { + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in P2P IIH from %s\n", circuit->interface->name); + } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in P2P IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + if (found & TLVFLAG_IPV6_ADDR) + { + /* TBA: check that we have a linklocal ourselves? */ + struct listnode *node; + struct in6_addr *ip; + for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) + if (IN6_IS_ADDR_LINKLOCAL (ip)) + { + v6_usable = 1; + break; + } + + if (!v6_usable) + zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local " + "in P2P IIH from %s\n", circuit->interface->name); + } + + if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) + zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n", + circuit->interface->name); +#endif + + if (!v6_usable && !v4_usable) { - zlog_warn ("ISIS-Adj: No usable IP interface addresses " - "in LAN IIH from %s\n", circuit->interface->name); free_tlvs (&tlvs); return ISIS_WARNING; } @@ -525,6 +566,17 @@ process_p2p_hello (struct isis_circuit *circuit) * the circuit */ adj = circuit->u.p2p.neighbor; + /* If an adjacency exists, check it is with the source of the hello + * packets */ + if (adj) + { + if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN)) + { + zlog_debug("hello source and adjacency do not match, set adj down\n"); + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "adj do not exist"); + return 0; + } + } if (!adj || adj->level != hdr->circuit_t) { if (!adj) @@ -550,9 +602,11 @@ process_p2p_hello (struct isis_circuit *circuit) tlvs_to_adj_area_addrs (&tlvs, adj); /* which protocol are spoken ??? */ - if (found & TLVFLAG_NLPID) - if (tlvs_to_adj_nlpids (&tlvs, adj)) - return ISIS_ERROR; + if (tlvs_to_adj_nlpids (&tlvs, adj)) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } /* we need to copy addresses to the adj */ if (found & TLVFLAG_IPV4_ADDR) @@ -850,6 +904,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) struct tlvs tlvs; u_char *snpa; struct listnode *node; + int v4_usable = 0, v6_usable = 0; if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -973,6 +1028,14 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) goto out; } + if (!(found & TLVFLAG_NLPID)) + { + zlog_warn ("No supported protocols TLV in Level %d LAN IS to IS hello", + level); + retval = ISIS_WARNING; + goto out; + } + /* Verify authentication, either cleartext of HMAC MD5 */ if (circuit->passwd.type) { @@ -988,6 +1051,13 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) } } + if (!memcmp (hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN)) + { + zlog_warn ("ISIS-Adj (%s): duplicate system ID on interface %s", + circuit->area->area_tag, circuit->interface->name); + return ISIS_WARNING; + } + /* * Accept the level 1 adjacency only if a match between local and * remote area addresses is found @@ -1021,14 +1091,48 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) /* * check if it's own interface ip match iih ip addrs */ - if ((found & TLVFLAG_IPV4_ADDR) == 0 || - ip_match (circuit->ip_addrs, tlvs.ipv4_addrs) == 0) + if (found & TLVFLAG_IPV4_ADDR) { - zlog_debug ("ISIS-Adj: No usable IP interface addresses " - "in LAN IIH from %s\n", circuit->interface->name); - retval = ISIS_WARNING; - goto out; + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in LAN IIH from %s\n", circuit->interface->name); } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in LAN IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + if (found & TLVFLAG_IPV6_ADDR) + { + /* TBA: check that we have a linklocal ourselves? */ + struct listnode *node; + struct in6_addr *ip; + for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) + if (IN6_IS_ADDR_LINKLOCAL (ip)) + { + v6_usable = 1; + break; + } + + if (!v6_usable) + zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local " + "in LAN IIH from %s\n", circuit->interface->name); + } + + if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) + zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n", + circuit->interface->name); +#endif + + if (!v6_usable && !v4_usable) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } + adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]); if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) || @@ -1096,8 +1200,11 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) tlvs_to_adj_area_addrs (&tlvs, adj); /* which protocol are spoken ??? */ - if (found & TLVFLAG_NLPID) - tlvs_to_adj_nlpids (&tlvs, adj); + if (tlvs_to_adj_nlpids (&tlvs, adj)) + { + retval = ISIS_WARNING; + goto out; + } /* we need to copy addresses to the adj */ if (found & TLVFLAG_IPV4_ADDR) diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 42947b22..4bc8717a 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -129,6 +129,7 @@ open_packet_socket (struct isis_circuit *circuit) sizeof (struct sockaddr_ll)) < 0) { zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno)); + close (fd); return ISIS_WARNING; } @@ -142,16 +143,14 @@ open_packet_socket (struct isis_circuit *circuit) * 8.4.2 - Broadcast subnetwork IIH PDUs * FIXME: is there a case only one will fail?? */ - if (circuit->is_type & IS_LEVEL_1) - /* joining ALL_L1_ISS */ - retval = isis_multicast_join (circuit->fd, 1, + /* joining ALL_L1_ISS */ + retval |= isis_multicast_join (circuit->fd, 1, circuit->interface->ifindex); - if (circuit->is_type & IS_LEVEL_2) - /* joining ALL_L2_ISS */ - retval = isis_multicast_join (circuit->fd, 2, + /* joining ALL_L2_ISS */ + retval |= isis_multicast_join (circuit->fd, 2, circuit->interface->ifindex); /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */ - retval = isis_multicast_join (circuit->fd, 3, + retval |= isis_multicast_join (circuit->fd, 3, circuit->interface->ifindex); } else diff --git a/isisd/isis_route.c b/isisd/isis_route.c index c99d9583..8ab470ce 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -438,7 +438,11 @@ isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth, route_node = route_node_get (area->route_table6[level - 1], prefix); #endif /* HAVE_IPV6 */ else - return NULL; + { + isis_route_info_delete (rinfo_new); + return NULL; + } + rinfo_old = route_node->info; if (!rinfo_old) { diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 558d3910..84a14ac5 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -69,8 +69,7 @@ isis_route_map_upd (const char *name) for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { if (isis->rmap[i].name) - isis->rmap[i].map = isis->rmap[i].map = - route_map_lookup_by_name (isis->rmap[i].name); + isis->rmap[i].map = route_map_lookup_by_name (isis->rmap[i].name); else isis->rmap[i].map = NULL; } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 07b855b4..fd93efa6 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -365,7 +365,7 @@ spftree_area_del (struct isis_area *area) area->spftree[1] = NULL; } #ifdef HAVE_IPV6 - if (area->spftree[1] != NULL) + if (area->spftree6[1] != NULL) { isis_spftree_del (area->spftree6[1]); area->spftree6[1] = NULL; @@ -833,7 +833,7 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, { assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN); - dist = cost + ip6reach->metric; + dist = cost + ntohl(ip6reach->metric); vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; prefix.prefixlen = ip6reach->prefix_len; @@ -1492,7 +1492,7 @@ isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid) struct listnode *anode; struct isis_vertex *vertex; struct isis_adjacency *adj; - u_char buff[255]; + u_char buff[BUFSIZ]; vty_out (vty, "Vertex Type Metric " "Next-Hop Interface Parent%s", VTY_NEWLINE); diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index f3b2c338..bbfa5d81 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -712,6 +712,7 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, Neighbor Extended Local Circuit ID (four octets, if Neighbor System ID is present) */ pnt += length; + value_len += length; } } else @@ -741,7 +742,6 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d", areatag, type, length); - retval = ISIS_WARNING; pnt += length; break; } @@ -932,10 +932,9 @@ tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream) { if (pos - value + IPV4_MAX_BYTELEN > 255) { - retval = add_tlv (IPV4_ADDR, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; + /* RFC 1195 s4.2: only one tuple of 63 allowed. */ + zlog_warn ("tlv_add_ip_addrs(): cutting off at 63 IP addresses"); + break; } *(u_int32_t *) pos = ipv4->prefix.s_addr; pos += IPV4_MAX_BYTELEN; diff --git a/isisd/isisd.c b/isisd/isisd.c index e8e3d9f0..ce6a2621 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2054,7 +2054,7 @@ validate_metric_style_narrow (struct vty *vty, struct isis_area *area) { if ((area->is_type & IS_LEVEL_1) && (circuit->is_type & IS_LEVEL_1) && - (circuit->metrics[0].metric_default > MAX_NARROW_LINK_METRIC)) + (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) { vty_out (vty, "ISIS circuit %s metric is invalid%s", circuit->interface->name, VTY_NEWLINE); @@ -2062,7 +2062,7 @@ validate_metric_style_narrow (struct vty *vty, struct isis_area *area) } if ((area->is_type & IS_LEVEL_2) && (circuit->is_type & IS_LEVEL_2) && - (circuit->metrics[1].metric_default > MAX_NARROW_LINK_METRIC)) + (circuit->te_metric[1] > MAX_NARROW_LINK_METRIC)) { vty_out (vty, "ISIS circuit %s metric is invalid%s", circuit->interface->name, VTY_NEWLINE); @@ -2092,19 +2092,22 @@ DEFUN (metric_style, area->newmetric = 1; area->oldmetric = 0; } - else if (strncmp (argv[0], "t", 1) == 0) - { - area->newmetric = 1; - area->oldmetric = 1; - } - else if (strncmp (argv[0], "n", 1) == 0) + else { ret = validate_metric_style_narrow (vty, area); if (ret != CMD_SUCCESS) return ret; - area->newmetric = 0; - area->oldmetric = 1; + if (strncmp (argv[0], "t", 1) == 0) + { + area->newmetric = 1; + area->oldmetric = 1; + } + else if (strncmp (argv[0], "n", 1) == 0) + { + area->newmetric = 0; + area->oldmetric = 1; + } } return CMD_SUCCESS; @@ -2857,6 +2860,11 @@ isis_config_write (struct vty *vty) vty_out (vty, " metric-style transition%s", VTY_NEWLINE); write++; } + else + { + vty_out (vty, " metric-style narrow%s", VTY_NEWLINE); + write++; + } /* ISIS - overload-bit */ if (area->overload_bit) { diff --git a/isisd/topology/Makefile.am b/isisd/topology/Makefile.am index ccd2e717..fe73ae5c 100644 --- a/isisd/topology/Makefile.am +++ b/isisd/topology/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" AM_CFLAGS = $(PICFLAGS) @@ -18,7 +18,4 @@ libtopology_a_LIBADD = @LIB_REGEX@ ../../lib/libzebra.la noinst_HEADERS = \ spgrid.h -depend: - @$(CPP) -MM $(INCLUDES) $(LDFLAGS) *.c - ## File dependency. diff --git a/lib/.gitignore b/lib/.gitignore index 00af85a6..02aa432c 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -5,6 +5,8 @@ Makefile.in *.la version.c version.h +gitversion.h +gitversion.h.tmp .deps .nfs* .libs diff --git a/lib/Makefile.am b/lib/Makefile.am index e00ad54d..5a806183 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" lib_LTLIBRARIES = libzebra.la @@ -14,11 +14,11 @@ libzebra_la_SOURCES = \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c -BUILT_SOURCES = memtypes.h route_types.h +BUILT_SOURCES = memtypes.h route_types.h gitversion.h libzebra_la_DEPENDENCIES = @LIB_REGEX@ -libzebra_la_LIBADD = @LIB_REGEX@ +libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@ pkginclude_HEADERS = \ buffer.h checksum.h command.h filter.h getopt.h hash.h \ @@ -27,12 +27,39 @@ pkginclude_HEADERS = \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \ privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \ - workqueue.h route_types.h + workqueue.h route_types.h libospf.h -EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.pl route_types.txt +EXTRA_DIST = \ + regex.c regex-gnu.h \ + queue.h \ + memtypes.awk \ + route_types.pl route_types.txt \ + gitversion.pl memtypes.h: $(srcdir)/memtypes.c $(srcdir)/memtypes.awk ($(GAWK) -f $(srcdir)/memtypes.awk $(srcdir)/memtypes.c > $@) route_types.h: $(srcdir)/route_types.txt $(srcdir)/route_types.pl @PERL@ $(srcdir)/route_types.pl < $(srcdir)/route_types.txt > $@ + +if GIT_VERSION + +# bit of a trick here to always have up-to-date git stamps without triggering +# unneccessary rebuilds. .PHONY causes the .tmp file to be rebuilt always, +# but if we use that on gitversion.h it'll ripple through the .c file deps. +# (even if gitversion.h's file timestamp doesn't change, make will think it +# did, because of .PHONY...) + +.PHONY: gitversion.h.tmp +.SILENT: gitversion.h gitversion.h.tmp +GITH=gitversion.h +gitversion.h.tmp: $(srcdir)/../.git + @PERL@ $(srcdir)/gitversion.pl $(srcdir) > ${GITH}.tmp +gitversion.h: gitversion.h.tmp + { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} + +else +.PHONY: gitversion.h +gitversion.h: + true +endif diff --git a/lib/command.c b/lib/command.c index 64563b5d..44cda628 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1,6 +1,8 @@ /* Command interpreter routine for virtual terminal [aka TeletYpe] Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + Copyright (C) 2013 by Open Source Routing. + Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") This file is part of GNU Zebra. @@ -35,9 +37,32 @@ Boston, MA 02111-1307, USA. */ each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct desc desc_cr; +struct cmd_token token_cr; char *command_cr = NULL; +enum filter_type +{ + FILTER_RELAXED, + FILTER_STRICT +}; + +enum matcher_rv +{ + MATCHER_OK, + MATCHER_COMPLETE, + MATCHER_INCOMPLETE, + MATCHER_NO_MATCH, + MATCHER_AMBIGUOUS, + MATCHER_EXCEED_ARGC_MAX +}; + +#define MATCHER_ERROR(matcher_rv) \ + ( (matcher_rv) == MATCHER_INCOMPLETE \ + || (matcher_rv) == MATCHER_NO_MATCH \ + || (matcher_rv) == MATCHER_AMBIGUOUS \ + || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ + ) + /* Host information structure. */ struct host host; @@ -84,7 +109,7 @@ static const char *default_motd = "\r\n\ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ " QUAGGA_COPYRIGHT "\r\n\ -\r\n"; +" GIT_INFO "\r\n"; static const struct facility_map { @@ -156,9 +181,10 @@ print_version (const char *progname) { printf ("%s version %s\n", progname, QUAGGA_VERSION); printf ("%s\n", QUAGGA_COPYRIGHT); + printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS); } - + /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * @@ -196,53 +222,6 @@ install_node (struct cmd_node *node, node->cmd_vector = vector_init (VECTOR_MIN_SIZE); } -/* Compare two command's string. Used in sort_node (). */ -static int -cmp_node (const void *p, const void *q) -{ - const struct cmd_element *a = *(struct cmd_element * const *)p; - const struct cmd_element *b = *(struct cmd_element * const *)q; - - return strcmp (a->string, b->string); -} - -static int -cmp_desc (const void *p, const void *q) -{ - const struct desc *a = *(struct desc * const *)p; - const struct desc *b = *(struct desc * const *)q; - - return strcmp (a->cmd, b->cmd); -} - -/* Sort each node's command element according to command string. */ -void -sort_node () -{ - unsigned int i, j; - struct cmd_node *cnode; - vector descvec; - struct cmd_element *cmd_element; - - for (i = 0; i < vector_active (cmdvec); i++) - if ((cnode = vector_slot (cmdvec, i)) != NULL) - { - vector cmd_vector = cnode->cmd_vector; - qsort (cmd_vector->index, vector_active (cmd_vector), - sizeof (void *), cmp_node); - - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && vector_active (cmd_element->strvec)) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - qsort (descvec->index, vector_active (descvec), - sizeof (void *), cmp_desc); - } - } -} - /* Breaking up string into each command piece. I assume given character is separated by a space character. Return value is a vector which includes char ** data element. */ @@ -312,15 +291,46 @@ cmd_free_strvec (vector v) vector_free (v); } -/* Fetch next description. Used in cmd_make_descvec(). */ +struct format_parser_state +{ + vector topvect; /* Top level vector */ + vector intvect; /* Intermediate level vector, used when there's + * a multiple in a keyword. */ + vector curvect; /* current vector where read tokens should be + appended. */ + + const char *string; /* pointer to command string, not modified */ + const char *cp; /* pointer in command string, moved along while + parsing */ + const char *dp; /* pointer in description string, moved along while + parsing */ + + int in_keyword; /* flag to remember if we are in a keyword group */ + int in_multiple; /* flag to remember if we are in a multiple group */ + int just_read_word; /* flag to remember if the last thing we red was a + * real word and not some abstract token */ +}; + +static void +format_parser_error(struct format_parser_state *state, const char *message) +{ + int offset = state->cp - state->string + 1; + + fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); + fprintf(stderr, " %*c\n", offset, '^'); + fprintf(stderr, "%s at offset %d.\n", message, offset); + fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); + exit(1); +} + static char * -cmd_desc_str (const char **string) +format_parser_desc_str(struct format_parser_state *state) { const char *cp, *start; char *token; int strlen; - - cp = *string; + + cp = state->dp; if (cp == NULL) return NULL; @@ -339,132 +349,226 @@ cmd_desc_str (const char **string) cp++; strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); + token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); memcpy (token, start, strlen); *(token + strlen) = '\0'; - *string = cp; + state->dp = cp; return token; } -/* New string vector. */ -static vector -cmd_make_descvec (const char *string, const char *descstr) +static void +format_parser_begin_keyword(struct format_parser_state *state) { - int multiple = 0; - const char *sp; - char *token; - int len; - const char *cp; - const char *dp; - vector allvec; - vector strvec = NULL; - struct desc *desc; + struct cmd_token *token; + vector keyword_vect; - cp = string; - dp = descstr; + if (state->in_keyword + || state->in_multiple) + format_parser_error(state, "Unexpected '{'"); - if (cp == NULL) - return NULL; + state->cp++; + state->in_keyword = 1; - allvec = vector_init (VECTOR_MIN_SIZE); + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_KEYWORD; + token->keyword = vector_init(VECTOR_MIN_SIZE); - while (1) - { - while (isspace ((int) *cp) && *cp != '\0') - cp++; + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(token->keyword, keyword_vect); - if (*cp == '(') - { - multiple = 1; - cp++; - } - if (*cp == ')') - { - multiple = 0; - cp++; - } - if (*cp == '|') - { - if (! multiple) - { - fprintf (stderr, "Command parse error!: %s\n", string); - exit (1); - } - cp++; - } - - while (isspace ((int) *cp) && *cp != '\0') - cp++; + vector_set(state->curvect, token); + state->curvect = keyword_vect; +} - if (*cp == '(') - { - multiple = 1; - cp++; - } +static void +format_parser_begin_multiple(struct format_parser_state *state) +{ + struct cmd_token *token; - if (*cp == '\0') - return allvec; + if (state->in_keyword == 1) + format_parser_error(state, "Keyword starting with '('"); - sp = cp; + if (state->in_multiple) + format_parser_error(state, "Nested group"); - while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') - cp++; + state->cp++; + state->in_multiple = 1; + state->just_read_word = 0; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_MULTIPLE; + token->multiple = vector_init(VECTOR_MIN_SIZE); + + vector_set(state->curvect, token); + if (state->curvect != state->topvect) + state->intvect = state->curvect; + state->curvect = token->multiple; +} - len = cp - sp; +static void +format_parser_end_keyword(struct format_parser_state *state) +{ + if (state->in_multiple + || !state->in_keyword) + format_parser_error(state, "Unexpected '}'"); - token = XMALLOC (MTYPE_STRVEC, len + 1); - memcpy (token, sp, len); - *(token + len) = '\0'; + if (state->in_keyword == 1) + format_parser_error(state, "Empty keyword group"); - desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str (&dp); + state->cp++; + state->in_keyword = 0; + state->curvect = state->topvect; +} - if (multiple) - { - if (multiple == 1) - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - multiple++; - } - else - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - vector_set (strvec, desc); +static void +format_parser_end_multiple(struct format_parser_state *state) +{ + char *dummy; + + if (!state->in_multiple) + format_parser_error(state, "Unepexted ')'"); + + if (vector_active(state->curvect) == 0) + format_parser_error(state, "Empty multiple section"); + + if (!state->just_read_word) + { + /* There are constructions like + * 'show ip ospf database ... (self-originate|)' + * in use. + * The old parser reads a description string for the + * word '' between |) which will never match. + * Simulate this behvaior by dropping the next desc + * string in such a case. */ + + dummy = format_parser_desc_str(state); + XFREE(MTYPE_CMD_TOKENS, dummy); } + + state->cp++; + state->in_multiple = 0; + + if (state->intvect) + state->curvect = state->intvect; + else + state->curvect = state->topvect; } -/* Count mandantory string vector size. This is to determine inputed - command has enough command length. */ -static int -cmd_cmdsize (vector strvec) +static void +format_parser_handle_pipe(struct format_parser_state *state) { - unsigned int i; - int size = 0; - vector descvec; - struct desc *desc; + struct cmd_token *keyword_token; + vector keyword_vect; - for (i = 0; i < vector_active (strvec); i++) - if ((descvec = vector_slot (strvec, i)) != NULL) + if (state->in_multiple) { - if ((vector_active (descvec)) == 1 - && (desc = vector_slot (descvec, 0)) != NULL) - { - if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) - return size; - else - size++; - } - else - size++; + state->just_read_word = 0; + state->cp++; + } + else if (state->in_keyword) + { + state->in_keyword = 1; + state->cp++; + + keyword_token = vector_slot(state->topvect, + vector_active(state->topvect) - 1); + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(keyword_token->keyword, keyword_vect); + state->curvect = keyword_vect; + } + else + { + format_parser_error(state, "Unexpected '|'"); + } +} + +static void +format_parser_read_word(struct format_parser_state *state) +{ + const char *start; + int len; + char *cmd; + struct cmd_token *token; + + start = state->cp; + + while (state->cp[0] != '\0' + && !strchr("\r\n(){}|", state->cp[0]) + && !isspace((int)state->cp[0])) + state->cp++; + + len = state->cp - start; + cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); + memcpy(cmd, start, len); + cmd[len] = '\0'; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_TERMINAL; + token->cmd = cmd; + token->desc = format_parser_desc_str(state); + vector_set(state->curvect, token); + + if (state->in_keyword == 1) + state->in_keyword = 2; + + state->just_read_word = 1; +} + +/** + * Parse a given command format string and build a tree of tokens from + * it that is suitable to be used by the command subsystem. + * + * @param string Command format string. + * @param descstr Description string. + * @return A vector of struct cmd_token representing the given command, + * or NULL on error. + */ +static vector +cmd_parse_format(const char *string, const char *descstr) +{ + struct format_parser_state state; + + if (string == NULL) + return NULL; + + memset(&state, 0, sizeof(state)); + state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); + state.cp = state.string = string; + state.dp = descstr; + + while (1) + { + while (isspace((int)state.cp[0]) && state.cp[0] != '\0') + state.cp++; + + switch (state.cp[0]) + { + case '\0': + if (state.in_keyword + || state.in_multiple) + format_parser_error(&state, "Unclosed group/keyword"); + return state.topvect; + case '{': + format_parser_begin_keyword(&state); + break; + case '(': + format_parser_begin_multiple(&state); + break; + case '}': + format_parser_end_keyword(&state); + break; + case ')': + format_parser_end_multiple(&state); + break; + case '|': + format_parser_handle_pipe(&state); + break; + default: + format_parser_read_word(&state); + } } - return size; } /* Return prompt character of specified node. */ @@ -497,11 +601,8 @@ install_element (enum node_type ntype, struct cmd_element *cmd) } vector_set (cnode->cmd_vector, cmd); - - if (cmd->strvec == NULL) - cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); - - cmd->cmdsize = cmd_cmdsize (cmd->strvec); + if (cmd->tokens == NULL) + cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); } static const unsigned char itoa64[] = @@ -847,9 +948,6 @@ cmd_ipv4_prefix_match (const char *str) static enum match_type cmd_ipv6_match (const char *str) { - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; @@ -1056,254 +1154,700 @@ cmd_range_match (const char *range, const char *str) return 1; } -/* Make completion match and return match type flag. */ static enum match_type -cmd_filter_by_completion (char *command, vector v, unsigned int index) +cmd_word_match(struct cmd_token *token, + enum filter_type filter, + const char *word) { - unsigned int i; const char *str; - struct cmd_element *cmd_element; enum match_type match_type; - vector descvec; - struct desc *desc; - - match_type = no_match; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + str = token->cmd; - descvec = vector_slot (cmd_element->strvec, index); + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; - - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; + if (!word) + return no_match; - matched++; - } - } + if (CMD_VARARG(str)) + { + return vararg_match; + } + else if (CMD_RANGE(str)) + { + if (cmd_range_match(str, word)) + return range_match; + } #ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + else if (CMD_IPV6(str)) + { + match_type = cmd_ipv6_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_match; + } + else if (CMD_IPV6_PREFIX(str)) + { + match_type = cmd_ipv6_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_prefix_match; + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) + { + match_type = cmd_ipv4_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_match; + } + else if (CMD_IPV4_PREFIX(str)) + { + match_type = cmd_ipv4_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_prefix_match; + } + else if (CMD_OPTION(str) || CMD_VARIABLE(str)) + { + return extend_match; + } + else + { + if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) + { + if (!strcmp(str, word)) + return exact_match; + return partly_match; + } + if (filter == FILTER_STRICT && !strcmp(str, word)) + return exact_match; + } - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command)) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; + return no_match; +} - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command)) - { - if (match_type < ipv4_match) - match_type = ipv4_match; +struct cmd_matcher +{ + struct cmd_element *cmd; /* The command element the matcher is using */ + enum filter_type filter; /* Whether to use strict or relaxed matching */ + vector vline; /* The tokenized commandline which is to be matched */ + unsigned int index; /* The index up to which matching should be done */ - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command)) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else - /* Check is this point's argument optional ? */ - if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else if (strncmp (command, str, strlen (command)) == 0) - { - if (strcmp (command, str) == 0) - match_type = exact_match; - else - { - if (match_type < partly_match) - match_type = partly_match; - } - matched++; - } - } - if (!matched) - vector_slot (v, i) = NULL; - } + /* If set, construct a list of matches at the position given by index */ + enum match_type *match_type; + vector *match; + + unsigned int word_index; /* iterating over vline */ +}; + +static int +push_argument(int *argc, const char **argv, const char *arg) +{ + if (!arg || !strlen(arg)) + arg = NULL; + + if (!argc || !argv) + return 0; + + if (*argc >= CMD_ARGC_MAX) + return -1; + + argv[(*argc)++] = arg; + return 0; +} + +static void +cmd_matcher_record_match(struct cmd_matcher *matcher, + enum match_type match_type, + struct cmd_token *token) +{ + if (matcher->word_index != matcher->index) + return; + + if (matcher->match) + { + if (!*matcher->match) + *matcher->match = vector_init(VECTOR_MIN_SIZE); + vector_set(*matcher->match, token); + } + + if (matcher->match_type) + { + if (match_type > *matcher->match_type) + *matcher->match_type = match_type; + } +} + +static int +cmd_matcher_words_left(struct cmd_matcher *matcher) +{ + return matcher->word_index < vector_active(matcher->vline); +} + +static const char* +cmd_matcher_get_word(struct cmd_matcher *matcher) +{ + assert(cmd_matcher_words_left(matcher)); + + return vector_slot(matcher->vline, matcher->word_index); +} + +static enum matcher_rv +cmd_matcher_match_terminal(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + const char *word; + enum match_type word_match; + + assert(token->type == TOKEN_TERMINAL); + + if (!cmd_matcher_words_left(matcher)) + { + if (CMD_OPTION(token->cmd)) + return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ + else + return MATCHER_INCOMPLETE; + } + + word = cmd_matcher_get_word(matcher); + word_match = cmd_word_match(token, matcher->filter, word); + if (word_match == no_match) + return MATCHER_NO_MATCH; + + /* We have to record the input word as argument if it matched + * against a variable. */ + if (CMD_VARARG(token->cmd) + || CMD_VARIABLE(token->cmd) + || CMD_OPTION(token->cmd)) + { + if (push_argument(argc, argv, word)) + return MATCHER_EXCEED_ARGC_MAX; + } + + cmd_matcher_record_match(matcher, word_match, token); + + matcher->word_index++; + + /* A vararg token should consume all left over words as arguments */ + if (CMD_VARARG(token->cmd)) + while (cmd_matcher_words_left(matcher)) + { + word = cmd_matcher_get_word(matcher); + if (word && strlen(word)) + push_argument(argc, argv, word); + matcher->word_index++; } - return match_type; + + return MATCHER_OK; } -/* Filter vector by command character with index. */ -static enum match_type -cmd_filter_by_string (char *command, vector v, unsigned int index) +static enum matcher_rv +cmd_matcher_match_multiple(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + enum match_type multiple_match; + unsigned int multiple_index; + const char *word; + const char *arg; + struct cmd_token *word_token; + enum match_type word_match; + + assert(token->type == TOKEN_MULTIPLE); + + multiple_match = no_match; + + if (!cmd_matcher_words_left(matcher)) + return MATCHER_INCOMPLETE; + + word = cmd_matcher_get_word(matcher); + for (multiple_index = 0; + multiple_index < vector_active(token->multiple); + multiple_index++) + { + word_token = vector_slot(token->multiple, multiple_index); + + word_match = cmd_word_match(word_token, matcher->filter, word); + if (word_match == no_match) + continue; + + cmd_matcher_record_match(matcher, word_match, word_token); + + if (word_match > multiple_match) + { + multiple_match = word_match; + arg = word; + } + /* To mimic the behavior of the old command implementation, we + * tolerate any ambiguities here :/ */ + } + + matcher->word_index++; + + if (multiple_match == no_match) + return MATCHER_NO_MATCH; + + if (push_argument(argc, argv, arg)) + return MATCHER_EXCEED_ARGC_MAX; + + return MATCHER_OK; +} + +static enum matcher_rv +cmd_matcher_read_keywords(struct cmd_matcher *matcher, + struct cmd_token *token, + vector args_vector) +{ + unsigned int i; + unsigned long keyword_mask; + unsigned int keyword_found; + enum match_type keyword_match; + enum match_type word_match; + vector keyword_vector; + struct cmd_token *word_token; + const char *word; + int keyword_argc; + const char **keyword_argv; + enum matcher_rv rv = MATCHER_NO_MATCH; + + keyword_mask = 0; + while (1) + { + if (!cmd_matcher_words_left(matcher)) + return MATCHER_OK; + + word = cmd_matcher_get_word(matcher); + + keyword_found = -1; + keyword_match = no_match; + for (i = 0; i < vector_active(token->keyword); i++) + { + if (keyword_mask & (1 << i)) + continue; + + keyword_vector = vector_slot(token->keyword, i); + word_token = vector_slot(keyword_vector, 0); + + word_match = cmd_word_match(word_token, matcher->filter, word); + if (word_match == no_match) + continue; + + cmd_matcher_record_match(matcher, word_match, word_token); + + if (word_match > keyword_match) + { + keyword_match = word_match; + keyword_found = i; + } + else if (word_match == keyword_match) + { + if (matcher->word_index != matcher->index || args_vector) + return MATCHER_AMBIGUOUS; + } + } + + if (keyword_found == (unsigned int)-1) + return MATCHER_NO_MATCH; + + matcher->word_index++; + + if (matcher->word_index > matcher->index) + return MATCHER_OK; + + keyword_mask |= (1 << keyword_found); + + if (args_vector) + { + keyword_argc = 0; + keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); + /* We use -1 as a marker for unused fields as NULL might be a valid value */ + for (i = 0; i < CMD_ARGC_MAX + 1; i++) + keyword_argv[i] = (void*)-1; + vector_set_index(args_vector, keyword_found, keyword_argv); + } + else + { + keyword_argv = NULL; + } + + keyword_vector = vector_slot(token->keyword, keyword_found); + /* the keyword itself is at 0. We are only interested in the arguments, + * so start counting at 1. */ + for (i = 1; i < vector_active(keyword_vector); i++) + { + word_token = vector_slot(keyword_vector, i); + + switch (word_token->type) + { + case TOKEN_TERMINAL: + rv = cmd_matcher_match_terminal(matcher, word_token, + &keyword_argc, keyword_argv); + break; + case TOKEN_MULTIPLE: + rv = cmd_matcher_match_multiple(matcher, word_token, + &keyword_argc, keyword_argv); + break; + case TOKEN_KEYWORD: + assert(!"Keywords should never be nested."); + break; + } + + if (MATCHER_ERROR(rv)) + return rv; + + if (matcher->word_index > matcher->index) + return MATCHER_OK; + } + } + /* not reached */ +} + +static enum matcher_rv +cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv, + vector keyword_args_vector) +{ + unsigned int i, j; + const char **keyword_args; + vector keyword_vector; + struct cmd_token *word_token; + const char *arg; + enum matcher_rv rv; + + rv = MATCHER_OK; + + if (keyword_args_vector == NULL) + return rv; + + for (i = 0; i < vector_active(token->keyword); i++) + { + keyword_vector = vector_slot(token->keyword, i); + keyword_args = vector_lookup(keyword_args_vector, i); + + if (vector_active(keyword_vector) == 1) + { + /* this is a keyword without arguments */ + if (keyword_args) + { + word_token = vector_slot(keyword_vector, 0); + arg = word_token->cmd; + } + else + { + arg = NULL; + } + + if (push_argument(argc, argv, arg)) + rv = MATCHER_EXCEED_ARGC_MAX; + } + else + { + /* this is a keyword with arguments */ + if (keyword_args) + { + /* the keyword was present, so just fill in the arguments */ + for (j = 0; keyword_args[j] != (void*)-1; j++) + if (push_argument(argc, argv, keyword_args[j])) + rv = MATCHER_EXCEED_ARGC_MAX; + XFREE(MTYPE_TMP, keyword_args); + } + else + { + /* the keyword was not present, insert NULL for the arguments + * the keyword would have taken. */ + for (j = 1; j < vector_active(keyword_vector); j++) + { + word_token = vector_slot(keyword_vector, j); + if ((word_token->type == TOKEN_TERMINAL + && (CMD_VARARG(word_token->cmd) + || CMD_VARIABLE(word_token->cmd) + || CMD_OPTION(word_token->cmd))) + || word_token->type == TOKEN_MULTIPLE) + { + if (push_argument(argc, argv, NULL)) + rv = MATCHER_EXCEED_ARGC_MAX; + } + } + } + } + } + vector_free(keyword_args_vector); + return rv; +} + +static enum matcher_rv +cmd_matcher_match_keyword(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + vector keyword_args_vector; + enum matcher_rv reader_rv; + enum matcher_rv builder_rv; + + assert(token->type == TOKEN_KEYWORD); + + if (argc && argv) + keyword_args_vector = vector_init(VECTOR_MIN_SIZE); + else + keyword_args_vector = NULL; + + reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); + builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, + argv, keyword_args_vector); + /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ + + if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) + return builder_rv; + + return reader_rv; +} + +static void +cmd_matcher_init(struct cmd_matcher *matcher, + struct cmd_element *cmd, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *match) +{ + memset(matcher, 0, sizeof(*matcher)); + + matcher->cmd = cmd; + matcher->filter = filter; + matcher->vline = vline; + matcher->index = index; + + matcher->match_type = match_type; + if (matcher->match_type) + *matcher->match_type = no_match; + matcher->match = match; + + matcher->word_index = 0; +} + +static enum matcher_rv +cmd_element_match(struct cmd_element *cmd_element, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *match, + int *argc, + const char **argv) +{ + struct cmd_matcher matcher; + unsigned int token_index; + enum matcher_rv rv = MATCHER_NO_MATCH; + + cmd_matcher_init(&matcher, cmd_element, filter, + vline, index, match_type, match); + + if (argc != NULL) + *argc = 0; + + for (token_index = 0; + token_index < vector_active(cmd_element->tokens); + token_index++) + { + struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); + + switch (token->type) + { + case TOKEN_TERMINAL: + rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); + break; + case TOKEN_MULTIPLE: + rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); + break; + case TOKEN_KEYWORD: + rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); + } + + if (MATCHER_ERROR(rv)) + return rv; + + if (matcher.word_index > index) + return MATCHER_OK; + } + + /* return MATCHER_COMPLETE if all words were consumed */ + if (matcher.word_index >= vector_active(vline)) + return MATCHER_COMPLETE; + + /* return MATCHER_COMPLETE also if only an empty word is left. */ + if (matcher.word_index == vector_active(vline) - 1 + && (!vector_slot(vline, matcher.word_index) + || !strlen((char*)vector_slot(vline, matcher.word_index)))) + return MATCHER_COMPLETE; + + return MATCHER_NO_MATCH; /* command is too long to match */ +} + +/** + * Filter a given vector of commands against a given commandline and + * calculate possible completions. + * + * @param commands A vector of struct cmd_element*. Commands that don't + * match against the given command line will be overwritten + * with NULL in that vector. + * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically + * determines how incomplete commands are handled, compare with + * cmd_word_match for details. + * @param vline A vector of char* containing the tokenized commandline. + * @param index Only match up to the given token of the commandline. + * @param match_type Record the type of the best match here. + * @param matches Record the matches here. For each cmd_element in the commands + * vector, a match vector will be created in the matches vector. + * That vector will contain all struct command_token* of the + * cmd_element which matched against the given vline at the given + * index. + * @return A code specifying if an error occured. If all went right, it's + * CMD_SUCCESS. + */ +static int +cmd_vector_filter(vector commands, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *matches) { unsigned int i; - const char *str; struct cmd_element *cmd_element; - enum match_type match_type; - vector descvec; - struct desc *desc; + enum match_type best_match; + enum match_type element_match; + enum matcher_rv matcher_rv; - match_type = no_match; + best_match = no_match; + *matches = vector_init(VECTOR_MIN_SIZE); - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_active (commands); i++) + if ((cmd_element = vector_slot (commands, i)) != NULL) { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + vector_set_index(*matches, i, NULL); + matcher_rv = cmd_element_match(cmd_element, filter, + vline, index, + &element_match, + (vector*)&vector_slot(*matches, i), + NULL, NULL); + if (MATCHER_ERROR(matcher_rv)) + { + vector_slot(commands, i) = NULL; + if (matcher_rv == MATCHER_AMBIGUOUS) + return CMD_ERR_AMBIGUOUS; + if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + else if (element_match > best_match) + { + best_match = element_match; + } + } + *match_type = best_match; + return CMD_SUCCESS; +} - descvec = vector_slot (cmd_element->strvec, index); +/** + * Check whether a given commandline is complete if used for a specific + * cmd_element. + * + * @param cmd_element A cmd_element against which the commandline should be + * checked. + * @param vline The tokenized commandline. + * @return 1 if the given commandline is complete, 0 otherwise. + */ +static int +cmd_is_complete(struct cmd_element *cmd_element, + vector vline) +{ + enum matcher_rv rv; + + rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + NULL, NULL); + return (rv == MATCHER_COMPLETE); +} - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; +/** + * Parse a given commandline and construct a list of arguments for the + * given command_element. + * + * @param cmd_element The cmd_element for which we want to construct arguments. + * @param vline The tokenized commandline. + * @param argc Where to store the argument count. + * @param argv Where to store the argument list. Should be at least + * CMD_ARGC_MAX elements long. + * @return CMD_SUCCESS if everything went alright, an error otherwise. + */ +static int +cmd_parse(struct cmd_element *cmd_element, + vector vline, + int *argc, const char **argv) +{ + enum matcher_rv rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + argc, argv); + switch (rv) + { + case MATCHER_COMPLETE: + return CMD_SUCCESS; - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command) == exact_match) - { - if (match_type < ipv6_match) - match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command) == exact_match) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command) == exact_match) - { - if (match_type < ipv4_match) - match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command) == exact_match) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else - { - if (strcmp (command, str) == 0) - { - match_type = exact_match; - matched++; - } - } - } - if (!matched) - vector_slot (v, i) = NULL; - } - } - return match_type; + case MATCHER_NO_MATCH: + return CMD_ERR_NO_MATCH; + + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + + case MATCHER_EXCEED_ARGC_MAX: + return CMD_ERR_EXEED_ARGC_MAX; + + default: + return CMD_ERR_INCOMPLETE; + } } /* Check ambiguous match */ static int -is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) +is_cmd_ambiguous (vector cmd_vector, + const char *command, + vector matches, + enum match_type type) { unsigned int i; unsigned int j; const char *str = NULL; - struct cmd_element *cmd_element; const char *matched = NULL; - vector descvec; - struct desc *desc; + vector match_vector; + struct cmd_token *cmd_token; - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + if (command == NULL) + command = ""; + + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i)) != NULL) { int match = 0; - descvec = vector_slot (cmd_element->strvec, index); - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) + for (j = 0; j < vector_active (match_vector); j++) + if ((cmd_token = vector_slot (match_vector, j)) != NULL) { enum match_type ret; - - str = desc->cmd; + + assert(cmd_token->type == TOKEN_TERMINAL); + if (cmd_token->type != TOKEN_TERMINAL) + continue; + + str = cmd_token->cmd; switch (type) { @@ -1371,7 +1915,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) } } if (!match) - vector_slot (v, i) = NULL; + vector_slot (cmd_vector, i) = NULL; } return 0; } @@ -1461,8 +2005,12 @@ cmd_entry_function_desc (const char *src, const char *dst) return NULL; } -/* Check same string element existence. If it isn't there return - 1. */ +/** + * Check whether a string is already present in a vector of strings. + * @param v A vector of char*. + * @param str A char*. + * @return 0 if str is already present in the vector, 1 otherwise. + */ static int cmd_unique_string (vector v, const char *str) { @@ -1476,19 +2024,25 @@ cmd_unique_string (vector v, const char *str) return 1; } -/* Compare string to description vector. If there is same string - return 1 else return 0. */ +/** + * Check whether a struct cmd_token matching a given string is already + * present in a vector of struct cmd_token. + * @param v A vector of struct cmd_token*. + * @param str A char* which should be searched for. + * @return 0 if there is a struct cmd_token* with its cmd matching str, + * 1 otherwise. + */ static int desc_unique_string (vector v, const char *str) { unsigned int i; - struct desc *desc; + struct cmd_token *token; for (i = 0; i < vector_active (v); i++) - if ((desc = vector_slot (v, i)) != NULL) - if (strcmp (desc->cmd, str) == 0) - return 1; - return 0; + if ((token = vector_slot (v, i)) != NULL) + if (strcmp (token->cmd, str) == 0) + return 0; + return 1; } static int @@ -1504,6 +2058,35 @@ cmd_try_do_shortcut (enum node_type node, char* first_word) { return 0; } +static void +cmd_matches_free(vector *matches) +{ + unsigned int i; + vector cmd_matches; + + for (i = 0; i < vector_active(*matches); i++) + if ((cmd_matches = vector_slot(*matches, i)) != NULL) + vector_free(cmd_matches); + vector_free(*matches); + *matches = NULL; +} + +static int +cmd_describe_cmp(const void *a, const void *b) +{ + const struct cmd_token *first = *(struct cmd_token * const *)a; + const struct cmd_token *second = *(struct cmd_token * const *)b; + + return strcmp(first->cmd, second->cmd); +} + +static void +cmd_describe_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_describe_cmp); +} + /* '?' describe command support. */ static vector cmd_describe_command_real (vector vline, struct vty *vty, int *status) @@ -1517,6 +2100,8 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) int ret; enum match_type match; char *command; + vector matches = NULL; + vector match_vector; /* Set index. */ if (vector_active (vline) == 0) @@ -1524,111 +2109,121 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) *status = CMD_ERR_NO_MATCH; return NULL; } - else - index = vector_active (vline) - 1; - + + index = vector_active (vline) - 1; + /* Make copy vector of current node's command vector. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); /* Prepare match vector */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Filter commands. */ - /* Only words precedes current word will be checked in this loop. */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - match = cmd_filter_by_completion (command, cmd_vector, i); - - if (match == vararg_match) - { - struct cmd_element *cmd_element; - vector descvec; - unsigned int j, k; + /* Filter commands and build a list how they could possibly continue. */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - for (k = 0; k < vector_active (descvec); k++) - { - struct desc *desc = vector_slot (descvec, k); - vector_set (matchvec, desc); - } - } - - vector_set (matchvec, &desc_cr); - vector_free (cmd_vector); + if (matches) + cmd_matches_free(&matches); - return matchvec; - } + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } + if (ret != CMD_SUCCESS) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + /* The last match may well be ambigious, so break here */ + if (i == index) + break; - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot (vline, index); - if (command) - match = cmd_filter_by_completion (command, cmd_vector, index); + if (match == vararg_match) + { + /* We found a vararg match - so we can throw out the current matches here + * and don't need to continue checking the command input */ + unsigned int j, k; + + for (j = 0; j < vector_active (matches); j++) + if ((match_vector = vector_slot (matches, j)) != NULL) + for (k = 0; k < vector_active (match_vector); k++) + { + struct cmd_token *token = vector_slot (match_vector, k); + vector_set (matchvec, token); + } + + *status = CMD_SUCCESS; + vector_set(matchvec, &token_cr); + vector_free (cmd_vector); + cmd_matches_free(&matches); + cmd_describe_sort(matchvec); + return matchvec; + } + + ret = is_cmd_ambiguous(cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + else if (ret == 2) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } /* Make description vector. */ - for (i = 0; i < vector_active (cmd_vector); i++) + for (i = 0; i < vector_active (matches); i++) if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) { - vector strvec = cmd_element->strvec; + unsigned int j; + const char *last_word; + vector vline_trimmed; - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - /* Check if command is completed. */ - if (command == NULL && index == vector_active (strvec)) - { - if (!desc_unique_string (matchvec, command_cr)) - vector_set (matchvec, &desc_cr); - } - else - { - unsigned int j; - vector descvec = vector_slot (strvec, index); - struct desc *desc; - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - const char *string; - - string = cmd_entry_function_desc (command, desc->cmd); - if (string) - { - /* Uniqueness check */ - if (!desc_unique_string (matchvec, string)) - vector_set (matchvec, desc); - } - } - } - } + last_word = vector_slot(vline, vector_active(vline) - 1); + if (last_word == NULL || !strlen(last_word)) + { + vline_trimmed = vector_copy(vline); + vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); + + if (cmd_is_complete(cmd_element, vline_trimmed) + && desc_unique_string(matchvec, command_cr)) + { + if (match != vararg_match) + vector_set(matchvec, &token_cr); + } + + vector_free(vline_trimmed); + } + + match_vector = vector_slot (matches, i); + if (match_vector) + for (j = 0; j < vector_active(match_vector); j++) + { + struct cmd_token *token = vector_slot(match_vector, j); + const char *string; + + string = cmd_entry_function_desc(command, token->cmd); + if (string && desc_unique_string(matchvec, string)) + vector_set(matchvec, token); + } } vector_free (cmd_vector); + cmd_matches_free(&matches); if (vector_slot (matchvec, 0) == NULL) { @@ -1638,6 +2233,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) } *status = CMD_SUCCESS; + cmd_describe_sort(matchvec); return matchvec; } @@ -1708,6 +2304,31 @@ cmd_lcd (char **matched) return lcd; } +static int +cmd_complete_cmp(const void *a, const void *b) +{ + const char *first = *(char * const *)a; + const char *second = *(char * const *)b; + + if (!first) + { + if (!second) + return 0; + return 1; + } + if (!second) + return -1; + + return strcmp(first, second); +} + +static void +cmd_complete_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_complete_cmp); +} + /* Command line completion support. */ static char ** cmd_complete_command_real (vector vline, struct vty *vty, int *status) @@ -1716,13 +2337,13 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); #define INIT_MATCHVEC_SIZE 10 vector matchvec; - struct cmd_element *cmd_element; unsigned int index; char **match_str; - struct desc *desc; - vector descvec; + struct cmd_token *token; char *command; int lcd; + vector matches = NULL; + vector match_vector; if (vector_active (vline) == 0) { @@ -1733,66 +2354,80 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) else index = vector_active (vline) - 1; - /* First, filter by preceeding command string */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - enum match_type match; - int ret; + /* First, filter by command string */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); + enum match_type match; + int ret; - /* First try completion match, if there is exactly match return 1 */ - match = cmd_filter_by_completion (command, cmd_vector, i); + if (matches) + cmd_matches_free(&matches); - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - /* + /* First try completion match, if there is exactly match return 1 */ + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + vector_free(cmd_vector); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } + + /* Break here - the completion mustn't be checked to be non-ambiguous */ + if (i == index) + break; + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* else if (ret == 2) { vector_free (cmd_vector); + cmd_matches_free(&matches); *status = CMD_ERR_NO_MATCH; return NULL; } */ - } + } /* Prepare match vector. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Now we got into completion */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) + /* Build the possible list of continuations into a list of completions */ + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i))) { const char *string; - vector strvec = cmd_element->strvec; - - /* Check field length */ - if (index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - unsigned int j; + unsigned int j; - descvec = vector_slot (strvec, index); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) + for (j = 0; j < vector_active (match_vector); j++) + if ((token = vector_slot (match_vector, j))) { if ((string = cmd_entry_function (vector_slot (vline, index), - desc->cmd))) + token->cmd))) if (cmd_unique_string (matchvec, string)) vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); } - } } /* We don't need cmd_vector any more. */ vector_free (cmd_vector); + cmd_matches_free(&matches); /* No matched command */ if (vector_slot (matchvec, 0) == NULL) @@ -1832,7 +2467,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) { char *lcdstr; - lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); + lcdstr = XMALLOC (MTYPE_TMP, lcd + 1); memcpy (lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; @@ -1842,7 +2477,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) for (i = 0; i < vector_active (matchvec); i++) { if (vector_slot (matchvec, i)) - XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); + XFREE (MTYPE_TMP, vector_slot (matchvec, i)); } vector_free (matchvec); @@ -1859,6 +2494,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) } match_str = (char **) matchvec->index; + cmd_complete_sort(matchvec); vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; return match_str; @@ -1927,7 +2563,9 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int -cmd_execute_command_real (vector vline, struct vty *vty, +cmd_execute_command_real (vector vline, + enum filter_type filter, + struct vty *vty, struct cmd_element **cmd) { unsigned int i; @@ -1939,35 +2577,48 @@ cmd_execute_command_real (vector vline, struct vty *vty, int argc; const char *argv[CMD_ARGC_MAX]; enum match_type match = 0; - int varflag; char *command; + int ret; + vector matches; /* Make copy of command elements. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_completion (command, cmd_vector, index); + { + command = vector_slot (vline, index); + ret = cmd_vector_filter(cmd_vector, + filter, + vline, index, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + cmd_matches_free(&matches); + return ret; + } - if (match == vararg_match) + if (match == vararg_match) + { + cmd_matches_free(&matches); break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); + } - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + cmd_matches_free(&matches); + + if (ret == 1) + { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + else if (ret == 2) + { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } /* Check matched count. */ matched_element = NULL; @@ -1977,12 +2628,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i))) { - if (match == vararg_match || index >= cmd_element->cmdsize) + if (cmd_is_complete(cmd_element, vline)) { matched_element = cmd_element; -#if 0 - printf ("DEBUG: %s\n", cmd_element->string); -#endif matched_count++; } else @@ -2006,35 +2654,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, if (matched_count > 1) return CMD_ERR_AMBIGUOUS; - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) - { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); - - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); - - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } + ret = cmd_parse(matched_element, vline, &argc, argv); + if (ret != CMD_SUCCESS) + return ret; /* For vtysh execution. */ if (cmd) @@ -2047,6 +2669,21 @@ cmd_execute_command_real (vector vline, struct vty *vty, return (*matched_element->func) (matched_element, vty, argc, argv); } +/** + * Execute a given command, handling things like "do ..." and checking + * whether the given command might apply at a parent node if doesn't + * apply for the current node. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @param vtysh If set != 0, don't lookup the command at parent nodes. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh) { @@ -2070,7 +2707,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_execute_command_real (shifted_vline, vty, cmd); + ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); vector_free(shifted_vline); vty->node = onode; @@ -2078,7 +2715,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, } - saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); + saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); if (vtysh) return saved_ret; @@ -2089,7 +2726,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, { try_node = node_parent(try_node); vty->node = try_node; - ret = cmd_execute_command_real (vline, vty, cmd); + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); tried = 1; if (ret == CMD_SUCCESS || ret == CMD_WARNING) { @@ -2104,134 +2741,37 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, return saved_ret; } -/* Execute command by argument readline. */ +/** + * Execute a given command, matching it strictly against the current node. + * This mode is used when reading config files. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element* of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command_strict (vector vline, struct vty *vty, struct cmd_element **cmd) { - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - int varflag; - enum match_type match = 0; - char *command; - - /* Make copy of command element */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_string (vector_slot (vline, index), - cmd_vector, index); - - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if (vector_slot (cmd_vector, i) != NULL) - { - cmd_element = vector_slot (cmd_vector, i); - - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) - { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); - - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); - - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; - - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } /* Configration make from file. */ int -config_from_file (struct vty *vty, FILE *fp) +config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) { int ret; + *line_num = 0; vector vline; while (fgets (vty->buf, VTY_BUFSIZ, fp)) { + ++(*line_num); vline = cmd_make_strvec (vty->buf); /* In case of comment line */ @@ -2333,6 +2873,7 @@ DEFUN (config_exit, case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PIM_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; @@ -2390,6 +2931,7 @@ DEFUN (config_end, case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -2409,7 +2951,9 @@ DEFUN (show_version, { vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", VTY_NEWLINE); - vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); + vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); + vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, + QUAGGA_CONFIG_ARGS, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2527,6 +3071,13 @@ DEFUN (config_write_file, VTY_NEWLINE); goto finished; } + +#if 0 + /* This code fails on UNION MOUNTs and similar filesystems if the + * config file is still on the RO layer. Hardlinks across layers + * will not work and cause quagga to fail saving the configuration... + * should use rename() to move files around... + */ if (link (config_file, config_file_sav) != 0) { vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, @@ -2540,7 +3091,23 @@ DEFUN (config_write_file, VTY_NEWLINE); goto finished; } +#else + /* And this is the code that hopefully does work */ + if (rename (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } + sync (); +#endif + +#if 0 + /* same here. Please no cross-filesystem hardlinks... */ if (link (config_file_tmp, config_file) != 0) +#else + if (rename (config_file_tmp, config_file) != 0) +#endif { vty_out (vty, "Can't save configuration file %s.%s", config_file, VTY_NEWLINE); @@ -3461,9 +4028,10 @@ install_default (enum node_type node) void cmd_init (int terminal) { - command_cr = XSTRDUP(MTYPE_STRVEC, ""); - desc_cr.cmd = command_cr; - desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); + command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); + token_cr.type = TOKEN_TERMINAL; + token_cr.cmd = command_cr; + token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -3584,14 +4152,61 @@ cmd_init (int terminal) srand(time(NULL)); } +static void +cmd_terminate_token(struct cmd_token *token) +{ + unsigned int i, j; + vector keyword_vect; + + if (token->multiple) + { + for (i = 0; i < vector_active(token->multiple); i++) + cmd_terminate_token(vector_slot(token->multiple, i)); + vector_free(token->multiple); + token->multiple = NULL; + } + + if (token->keyword) + { + for (i = 0; i < vector_active(token->keyword); i++) + { + keyword_vect = vector_slot(token->keyword, i); + for (j = 0; j < vector_active(keyword_vect); j++) + cmd_terminate_token(vector_slot(keyword_vect, j)); + vector_free(keyword_vect); + } + vector_free(token->keyword); + token->keyword = NULL; + } + + XFREE(MTYPE_CMD_TOKENS, token->cmd); + XFREE(MTYPE_CMD_TOKENS, token->desc); + + XFREE(MTYPE_CMD_TOKENS, token); +} + +static void +cmd_terminate_element(struct cmd_element *cmd) +{ + unsigned int i; + + if (cmd->tokens == NULL) + return; + + for (i = 0; i < vector_active(cmd->tokens); i++) + cmd_terminate_token(vector_slot(cmd->tokens, i)); + + vector_free(cmd->tokens); + cmd->tokens = NULL; +} + void cmd_terminate () { - unsigned int i, j, k, l; + unsigned int i, j; struct cmd_node *cmd_node; struct cmd_element *cmd_element; - struct desc *desc; - vector cmd_node_v, cmd_element_v, desc_v; + vector cmd_node_v; if (cmdvec) { @@ -3601,30 +4216,8 @@ cmd_terminate () cmd_node_v = cmd_node->cmd_vector; for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && - cmd_element->strvec != NULL) - { - cmd_element_v = cmd_element->strvec; - - for (k = 0; k < vector_active (cmd_element_v); k++) - if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) - { - for (l = 0; l < vector_active (desc_v); l++) - if ((desc = vector_slot (desc_v, l)) != NULL) - { - if (desc->cmd) - XFREE (MTYPE_STRVEC, desc->cmd); - if (desc->str) - XFREE (MTYPE_STRVEC, desc->str); - - XFREE (MTYPE_DESC, desc); - } - vector_free (desc_v); - } - - cmd_element->strvec = NULL; - vector_free (cmd_element_v); - } + if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) + cmd_terminate_element(cmd_element); vector_free (cmd_node_v); } @@ -3634,9 +4227,9 @@ cmd_terminate () } if (command_cr) - XFREE(MTYPE_STRVEC, command_cr); - if (desc_cr.str) - XFREE(MTYPE_STRVEC, desc_cr.str); + XFREE(MTYPE_CMD_TOKENS, command_cr); + if (token_cr.desc) + XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) diff --git a/lib/command.h b/lib/command.h index 2d708d8e..8eb0cbd9 100644 --- a/lib/command.h +++ b/lib/command.h @@ -88,6 +88,7 @@ enum node_type OSPF_NODE, /* OSPF protocol mode */ OSPF6_NODE, /* OSPF protocol for IPv6 mode */ ISIS_NODE, /* ISIS protocol mode */ + PIM_NODE, /* PIM protocol mode */ MASC_NODE, /* MASC for multicast. */ IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ IP_NODE, /* Static ip route node. */ @@ -138,18 +139,32 @@ struct cmd_element int (*func) (struct cmd_element *, struct vty *, int, const char *[]); const char *doc; /* Documentation of this command. */ int daemon; /* Daemon to which this command belong. */ - vector strvec; /* Pointing out each description vector. */ - unsigned int cmdsize; /* Command index count. */ - char *config; /* Configuration string */ - vector subconfig; /* Sub configuration string */ + vector tokens; /* Vector of cmd_tokens */ u_char attr; /* Command attributes */ }; + +enum cmd_token_type +{ + TOKEN_TERMINAL = 0, + TOKEN_MULTIPLE, + TOKEN_KEYWORD, +}; + /* Command description structure. */ -struct desc +struct cmd_token { + enum cmd_token_type type; + + /* Used for type == MULTIPLE */ + vector multiple; /* vector of cmd_token, type == FINAL */ + + /* Used for type == KEYWORD */ + vector keyword; /* vector of vector of cmd_tokens */ + + /* Used for type == TERMINAL */ char *cmd; /* Command string. */ - char *str; /* Command's description. */ + char *desc; /* Command's description. */ }; /* Return value of the commands. */ @@ -192,7 +207,170 @@ struct desc int argc __attribute__ ((unused)), \ const char *argv[] __attribute__ ((unused)) ) -/* DEFUN for vty command interafce. Little bit hacky ;-). */ +/* DEFUN for vty command interafce. Little bit hacky ;-). + * + * DEFUN(funcname, cmdname, cmdstr, helpstr) + * + * funcname + * ======== + * + * Name of the function that will be defined. + * + * cmdname + * ======= + * + * Name of the struct that will be defined for the command. + * + * cmdstr + * ====== + * + * The cmdstr defines the command syntax. It is used by the vty subsystem + * and vtysh to perform matching and completion in the cli. So you have to take + * care to construct it adhering to the following grammar. The names used + * for the production rules losely represent the names used in lib/command.c + * + * cmdstr = cmd_token , { " " , cmd_token } ; + * + * cmd_token = cmd_terminal + * | cmd_multiple + * | cmd_keyword ; + * + * cmd_terminal_fixed = fixed_string + * | variable + * | range + * | ipv4 + * | ipv4_prefix + * | ipv6 + * | ipv6_prefix ; + * + * cmd_terminal = cmd_terminal_fixed + * | option + * | vararg ; + * + * multiple_part = cmd_terminal_fixed ; + * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; + * + * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; + * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; + * + * lowercase = "a" | ... | "z" ; + * uppercase = "A" | ... | "Z" ; + * digit = "0" | ... | "9" ; + * number = digit , { digit } ; + * + * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; + * variable = uppercase , { uppercase | "_" } ; + * range = "<" , number , "-" , number , ">" ; + * ipv4 = "A.B.C.D" ; + * ipv4_prefix = "A.B.C.D/M" ; + * ipv6 = "X:X::X:X" ; + * ipv6_prefix = "X:X::X:X/M" ; + * option = "[" , variable , "]" ; + * vararg = "." , variable ; + * + * To put that all in a textual description: A cmdstr is a sequence of tokens, + * separated by spaces. + * + * Terminal Tokens: + * + * A very simple cmdstring would be something like: "show ip bgp". It consists + * of three Terminal Tokens, each containing a fixed string. When this command + * is called, no arguments will be passed down to the function implementing it, + * as it only consists of fixed strings. + * + * Apart from fixed strings, Terminal Tokens can also contain variables: + * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 + * as argument. As this is a variable, the IP address entered by the user will + * be passed down as an argument. Apart from two exceptions, the other options + * for Terminal Tokens behave exactly as we just discussed and only make a + * difference for the CLI. The two exceptions will be discussed in the next + * paragraphs. + * + * A Terminal Token can contain a so called option match. This is a simple + * string variable that the user may omit. An example would be: + * "show interface [IFNAME]". If the user calls this without an interface as + * argument, no arguments will be passed down to the function implementing + * this command. Otherwise, the interface name will be provided to the function + * as a regular argument. + + * Also, a Terminal Token can contain a so called vararg. This is used e.g. in + * "show ip bgp regexp .LINE". The last token is a vararg match and will + * consume all the arguments the user inputs on the command line and append + * those to the list of arguments passed down to the function implementing this + * command. (Therefore, it doesn't make much sense to have any tokens after a + * vararg because the vararg will already consume all the words the user entered + * in the CLI) + * + * Multiple Tokens: + * + * The Multiple Token type can be used if there are multiple possibilities what + * arguments may be used for a command, but it should map to the same function + * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" + * In that case both "reject" and "blackhole" would be acceptable as last + * arguments. The words matched by Multiple Tokens are always added to the + * argument list, even if they are matched by fixed strings. Such a Multiple + * Token can contain almost any type of token that would also be acceptable + * for a Terminal Token, the exception are optional variables and varag. + * + * There is one special case that is used in some places of Quagga that should be + * pointed out here shortly. An example would be "password (8|) WORD". This + * construct is used to have fixed strings communicated as arguments. (The "8" + * will be passed down as an argument in this case) It does not mean that + * the "8" is optional. Another historic and possibly surprising property of + * this construct is that it consumes two parts of helpstr. (Help + * strings will be explained later) + * + * Keyword Tokens: + * + * There are commands that take a lot of different and possibly optional arguments. + * An example from ospf would be the "default-information originate" command. This + * command takes a lot of optional arguments that may be provided in any order. + * To accomodate such commands, the Keyword Token has been implemented. + * Using the keyword token, the "default-information originate" command and all + * its possible options can be represented using this single cmdstr: + * "default-information originate \ + * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" + * + * Keywords always start with a fixed string and may be followed by arguments. + * Except optional variables and vararg, everything is permitted here. + * + * For the special case of a keyword without arguments, either NULL or the + * keyword itself will be pushed as an argument, depending on whether the + * keyword is present. + * For the other keywords, arguments will be only pushed for + * variables/Multiple Tokens. If the keyword is not present, the arguments that + * would have been pushed will be substituted by NULL. + * + * A few examples: + * "default information originate metric-type 1 metric 1000" + * would yield the following arguments: + * { NULL, "1000", "1", NULL } + * + * "default information originate always route-map RMAP-DEFAULT" + * would yield the following arguments: + * { "always", NULL, NULL, "RMAP-DEFAULT" } + * + * helpstr + * ======= + * + * The helpstr is used to show a short explantion for the commands that + * are available when the user presses '?' on the CLI. It is the concatenation + * of the helpstrings for all the tokens that make up the command. + * + * There should be one helpstring for each token in the cmdstr except those + * containing other tokens, like Multiple or Keyword Tokens. For those, there + * will only be the helpstrings of the contained tokens. + * + * The individual helpstrings are expected to be in the same order as their + * respective Tokens appear in the cmdstr. They should each be terminated with + * a linefeed. The last helpstring should be terminated with a linefeed as well. + * + * Care should also be taken to avoid having similar tokens with different + * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". + * they both contain a helpstring for "show", but only one will be displayed + * when the user enters "sh?". If those two helpstrings differ, it is not + * defined which one will be shown and the behavior is therefore unpredictable. + */ #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -330,7 +508,6 @@ struct desc extern void install_node (struct cmd_node *, int (*) (struct vty *)); extern void install_default (enum node_type); extern void install_element (enum node_type, struct cmd_element *); -extern void sort_node (void); /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using @@ -342,11 +519,10 @@ extern void cmd_free_strvec (vector); extern vector cmd_describe_command (vector, struct vty *, int *status); extern char **cmd_complete_command (vector, struct vty *, int *status); extern const char *cmd_prompt (enum node_type); -extern int config_from_file (struct vty *, FILE *); +extern int config_from_file (struct vty *, FILE *, unsigned int *line_num); extern enum node_type node_parent (enum node_type); extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); -extern void config_replace_string (struct cmd_element *, char *, ...); extern void cmd_init (int); extern void cmd_terminate (void); diff --git a/lib/distribute.c b/lib/distribute.c index 8d6f6377..ba8043cf 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -34,7 +34,7 @@ struct hash *disthash; /* Hook functions. */ void (*distribute_add_hook) (struct distribute *); void (*distribute_delete_hook) (struct distribute *); - + static struct distribute * distribute_new (void) { @@ -133,7 +133,7 @@ distribute_cmp (const struct distribute *dist1, const struct distribute *dist2) return 1; return 0; } - + /* Set access-list name to the distribute list. */ static struct distribute * distribute_list_set (const char *ifname, enum distribute_type type, diff --git a/lib/filter.c b/lib/filter.c index 69341824..96605c7d 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -110,7 +110,7 @@ static struct access_master access_master_ipv6 = NULL, }; #endif /* HAVE_IPV6 */ - + static struct access_master * access_master_get (afi_t afi) { @@ -208,7 +208,7 @@ filter_match_zebra (struct filter *mfilter, struct prefix *p) else return 0; } - + /* Allocate new access list structure. */ static struct access_list * access_list_new (void) @@ -501,7 +501,7 @@ access_list_filter_delete (struct access_list *access, struct filter *filter) if (master->delete_hook) (*master->delete_hook) (access); } - + /* deny Specify packets to reject permit Specify packets to forward diff --git a/lib/getopt.c b/lib/getopt.c index c784fb6c..064909d3 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -23,7 +23,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO @@ -193,7 +193,7 @@ static enum /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; - + #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. @@ -243,7 +243,7 @@ extern int strlen (const char *); #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ - + /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have @@ -456,7 +456,7 @@ _getopt_initialize (argc, argv, optstring) return optstring; } - + /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. @@ -986,7 +986,7 @@ getopt (argc, argv, optstring) #endif /* REALLY_NEED_PLAIN_GETOPT */ #endif /* Not ELIDE_CODE. */ - + #ifdef TEST /* Compile with -DTEST to make an executable for use in testing diff --git a/lib/getopt1.c b/lib/getopt1.c index 985f12c5..fa766747 100644 --- a/lib/getopt1.c +++ b/lib/getopt1.c @@ -19,7 +19,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + #ifdef HAVE_CONFIG_H #include #endif @@ -95,7 +95,7 @@ getopt_long_only (argc, argv, options, long_options, opt_index) #endif /* Not ELIDE_CODE. */ - + #ifdef TEST #include diff --git a/lib/gitversion.pl b/lib/gitversion.pl new file mode 100644 index 00000000..8ddd9ffa --- /dev/null +++ b/lib/gitversion.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w +use strict; + +my $dir = shift; +chdir $dir || die "$dir: $!\n"; + +my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`; +chomp $gitdesc; +my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; + +printf STDERR "git suffix: %s\n", $gitsuffix; +printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix; + +my $gitcommit = `git log -1 --format=\"%H\" || echo DEADBEEF`; +chomp $gitcommit; +open(BRANCHES, "git branch -a -v --abbrev=40|") || die "git branch: $!\n"; +my @names = (); +while () { + chomp $_; + if (/\s+(.*?)\s+$gitcommit/) { + my $branch = $1; + if ($branch =~ /^remotes\/(.*?)(\/.*)$/) { + my $path = $2; + my $url = `git config --get "remote.$1.url"`; + chomp $url; + $url =~ s/^(git:|https?:|git@)\/\/github\.com/github/i; + $url =~ s/^(ssh|git):\/\/git\.sv\.gnu\.org\/srv\/git\//savannah:/i; + $url =~ s/^(ssh|git):\/\/git\.savannah\.nongnu\.org\//savannah:/i; + + push @names, $url.$path; + } else { + push @names, 'local:'.$branch; + } + } +} + +printf STDERR "git branches: %s\n", join(", ", @names); + +my $cr = "\\r\\n\\"; +printf <index = XCALLOC (MTYPE_HASH_INDEX, sizeof (struct hash_backet *) * size); hash->size = size; + hash->no_expand = 0; hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; @@ -47,7 +49,7 @@ struct hash * hash_create (unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { - return hash_create_size (HASHTABSIZE, hash_key, hash_cmp); + return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp); } /* Utility function for hash_get(). When this function is specified @@ -59,6 +61,52 @@ hash_alloc_intern (void *arg) return arg; } +/* Expand hash if the chain length exceeds the threshold. */ +static void hash_expand (struct hash *hash) +{ + unsigned int i, new_size, losers; + struct hash_backet *hb, *hbnext, **new_index; + + new_size = hash->size * 2; + new_index = XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_backet *) * new_size); + if (new_index == NULL) + return; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hbnext) + { + unsigned int h = hb->key & (new_size - 1); + + hbnext = hb->next; + hb->next = new_index[h]; + new_index[h] = hb; + } + + /* Switch to new table */ + XFREE(MTYPE_HASH_INDEX, hash->index); + hash->size = new_size; + hash->index = new_index; + + /* Ideally, new index should have chains half as long as the original. + If expansion didn't help, then not worth expanding again, + the problem is the hash function. */ + losers = 0; + for (i = 0; i < hash->size; i++) + { + unsigned int len = 0; + for (hb = hash->index[i]; hb; hb = hb->next) + { + if (++len > HASH_THRESHOLD/2) + ++losers; + if (len >= HASH_THRESHOLD) + hash->no_expand = 1; + } + } + + if (losers > hash->count / 2) + hash->no_expand = 1; +} + /* Lookup and return hash backet in hash. If there is no corresponding hash backet and alloc_func is specified, create new hash backet. */ @@ -68,14 +116,19 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) unsigned int key; unsigned int index; void *newdata; + unsigned int len; struct hash_backet *backet; key = (*hash->hash_key) (data); - index = key % hash->size; + index = key & (hash->size - 1); + len = 0; - for (backet = hash->index[index]; backet != NULL; backet = backet->next) - if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) - return backet->data; + for (backet = hash->index[index]; backet != NULL; backet = backet->next) + { + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + return backet->data; + ++len; + } if (alloc_func) { @@ -83,6 +136,12 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) if (newdata == NULL) return NULL; + if (len > HASH_THRESHOLD && !hash->no_expand) + { + hash_expand (hash); + index = key & (hash->size - 1); + } + backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; @@ -125,7 +184,7 @@ hash_release (struct hash *hash, void *data) struct hash_backet *pp; key = (*hash->hash_key) (data); - index = key % hash->size; + index = key & (hash->size - 1); for (backet = pp = hash->index[index]; backet; backet = backet->next) { diff --git a/lib/hash.h b/lib/hash.h index 4cb772e5..920c6685 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -22,7 +22,8 @@ Boston, MA 02111-1307, USA. */ #define _ZEBRA_HASH_H /* Default hash table size. */ -#define HASHTABSIZE 1024 +#define HASH_INITIAL_SIZE 256 /* initial number of backets. */ +#define HASH_THRESHOLD 10 /* expand when backet. */ struct hash_backet { @@ -41,9 +42,12 @@ struct hash /* Hash backet. */ struct hash_backet **index; - /* Hash table size. */ + /* Hash table size. Must be power of 2 */ unsigned int size; + /* If expansion failed. */ + int no_expand; + /* Key make function. */ unsigned int (*hash_key) (void *); diff --git a/lib/if.c b/lib/if.c index e26aa046..3a1f9b41 100644 --- a/lib/if.c +++ b/lib/if.c @@ -35,7 +35,7 @@ #include "buffer.h" #include "str.h" #include "log.h" - + /* Master list of interfaces. */ struct list *iflist; @@ -45,7 +45,7 @@ struct if_master int (*if_new_hook) (struct interface *); int (*if_delete_hook) (struct interface *); } if_master; - + /* Compare interface names, returning an integer greater than, equal to, or * less than 0, (following the strcmp convention), according to the * relationship between ifp1 and ifp2. Interface names consist of an @@ -304,6 +304,28 @@ if_lookup_address (struct in_addr src) return match; } +/* Lookup interface by prefix */ +struct interface * +if_lookup_prefix (struct prefix *prefix) +{ + struct listnode *node; + struct listnode *cnode; + struct interface *ifp; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if (prefix_cmp(c->address, prefix) == 0) + { + return ifp; + } + } + } + return NULL; +} + /* Get interface by name if given name interface doesn't exist create one. */ struct interface * @@ -429,7 +451,7 @@ static void if_dump (const struct interface *ifp) { struct listnode *node; - struct connected *c; + struct connected *c __attribute__((unused)); for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c)) zlog_info ("Interface %s index %d metric %d mtu %d " @@ -489,7 +511,7 @@ DEFUN (no_interface_desc, return CMD_SUCCESS; } - + #ifdef SUNOS_5 /* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created * a seperate struct interface for each logical interface, so config @@ -531,7 +553,7 @@ if_sunwzebra_get (const char *name, size_t nlen) return if_get_by_name_len (name, nlen); } #endif /* SUNOS_5 */ - + DEFUN (interface, interface_cmd, "interface IFNAME", @@ -782,7 +804,7 @@ if_indextoname (unsigned int ifindex, char *name) return ifp->name; } #endif - + #if 0 /* this route_table of struct connected's is unused * however, it would be good to use a route_table rather than * a list.. diff --git a/lib/if.h b/lib/if.h index 841ce51e..8081be87 100644 --- a/lib/if.h +++ b/lib/if.h @@ -103,7 +103,13 @@ struct interface /* Hardware address. */ #ifdef HAVE_STRUCT_SOCKADDR_DL - struct sockaddr_dl sdl; + union { + /* note that sdl_storage is never accessed, it only exists to make space. + * all actual uses refer to sdl - but use sizeof(sdl_storage)! this fits + * best with C aliasing rules. */ + struct sockaddr_dl sdl; + struct sockaddr_storage sdl_storage; + }; #else unsigned short hw_type; u_char hw_addr[INTERFACE_HWADDR_MAX]; @@ -145,11 +151,16 @@ struct connected u_char conf; #define ZEBRA_IFC_REAL (1 << 0) #define ZEBRA_IFC_CONFIGURED (1 << 1) +#define ZEBRA_IFC_QUEUED (1 << 2) /* The ZEBRA_IFC_REAL flag should be set if and only if this address - exists in the kernel. + exists in the kernel and is actually usable. (A case where it exists but + is not yet usable would be IPv6 with DAD) The ZEBRA_IFC_CONFIGURED flag should be set if and only if this address was configured by the user from inside quagga. + The ZEBRA_IFC_QUEUED flag should be set if and only if the address exists + in the kernel. It may and should be set although the address might not be + usable yet. (compare with ZEBRA_IFC_REAL) */ /* Flags for connected address. */ @@ -234,6 +245,7 @@ extern struct interface *if_create (const char *name, int namelen); extern struct interface *if_lookup_by_index (unsigned int); extern struct interface *if_lookup_exact_address (struct in_addr); extern struct interface *if_lookup_address (struct in_addr); +extern struct interface *if_lookup_prefix (struct prefix *prefix); /* These 2 functions are to be used when the ifname argument is terminated by a '\0' character: */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 7d049b87..e4a83de8 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -32,7 +32,7 @@ struct hash *ifrmaphash; /* Hook functions. */ static void (*if_rmap_add_hook) (struct if_rmap *) = NULL; static void (*if_rmap_delete_hook) (struct if_rmap *) = NULL; - + static struct if_rmap * if_rmap_new (void) { @@ -122,7 +122,7 @@ if_rmap_hash_cmp (const void *arg1, const void* arg2) return strcmp (if_rmap1->ifname, if_rmap2->ifname) == 0; } - + static struct if_rmap * if_rmap_set (const char *ifname, enum if_rmap_type type, const char *routemap_name) @@ -273,7 +273,7 @@ ALIAS (no_if_rmap, "Route map for input filtering\n" "Route map for output filtering\n" "Route map interface name\n") - + /* Configuration write function. */ int config_write_if_rmap (struct vty *vty) diff --git a/lib/jhash.c b/lib/jhash.c index 071fed6e..6154c346 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -42,10 +42,10 @@ * the input key. */ u_int32_t -jhash (void *key, u_int32_t length, u_int32_t initval) +jhash (const void *key, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; - u_int8_t *k = key; + const u_int8_t *k = key; len = length; a = b = JHASH_GOLDEN_RATIO; @@ -105,7 +105,7 @@ jhash (void *key, u_int32_t length, u_int32_t initval) * The length parameter here is the number of u_int32_ts in the key. */ u_int32_t -jhash2 (u_int32_t * k, u_int32_t length, u_int32_t initval) +jhash2 (const u_int32_t *k, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; diff --git a/lib/jhash.h b/lib/jhash.h index 44dd1b56..985ac94e 100644 --- a/lib/jhash.h +++ b/lib/jhash.h @@ -24,12 +24,12 @@ * of bytes. No alignment or length assumptions are made about * the input key. */ -extern u_int32_t jhash(void *key, u_int32_t length, u_int32_t initval); +extern u_int32_t jhash(const void *key, u_int32_t length, u_int32_t initval); /* A special optimized version that handles 1 or more of u_int32_ts. * The length parameter here is the number of u_int32_ts in the key. */ -extern u_int32_t jhash2(u_int32_t *k, u_int32_t length, u_int32_t initval); +extern u_int32_t jhash2(const u_int32_t *k, u_int32_t length, u_int32_t initval); /* A special ultra-optimized versions that knows they are hashing exactly * 3, 2 or 1 word(s). diff --git a/lib/keychain.c b/lib/keychain.c index 6719cebf..762c4629 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -226,7 +226,7 @@ key_delete (struct keychain *keychain, struct key *key) free (key->string); key_free (key); } - + DEFUN (key_chain, key_chain_cmd, "key chain WORD", @@ -531,7 +531,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange, return CMD_SUCCESS; } - + DEFUN (accept_lifetime_day_month_day_month, accept_lifetime_day_month_day_month_cmd, "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", @@ -689,7 +689,7 @@ DEFUN (accept_lifetime_duration_month_day, return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3], argv[4]); } - + DEFUN (send_lifetime_day_month_day_month, send_lifetime_day_month_day_month_cmd, "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", @@ -847,7 +847,7 @@ DEFUN (send_lifetime_duration_month_day, return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], argv[4]); } - + static struct cmd_node keychain_node = { KEYCHAIN_NODE, diff --git a/lib/libospf.h b/lib/libospf.h new file mode 100644 index 00000000..2796209d --- /dev/null +++ b/lib/libospf.h @@ -0,0 +1,92 @@ +/* + * Defines and structures common to OSPFv2 and OSPFv3 + * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBOSPFD_H +#define _LIBOSPFD_H + +/* IP precedence. */ +#ifndef IPTOS_PREC_INTERNETCONTROL +#define IPTOS_PREC_INTERNETCONTROL 0xC0 +#endif /* IPTOS_PREC_INTERNETCONTROL */ + +/* Default protocol, port number. */ +#ifndef IPPROTO_OSPFIGP +#define IPPROTO_OSPFIGP 89 +#endif /* IPPROTO_OSPFIGP */ + +/* Architectual Constants */ +#ifdef DEBUG +#define OSPF_LS_REFRESH_TIME 60 +#else +#define OSPF_LS_REFRESH_TIME 1800 +#endif +#define OSPF_MIN_LS_INTERVAL 5 +#define OSPF_MIN_LS_ARRIVAL 1 +#define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ +#define OSPF_LSA_MAXAGE 3600 +#define OSPF_CHECK_AGE 300 +#define OSPF_LSA_MAXAGE_DIFF 900 +#define OSPF_LS_INFINITY 0xffffff +#define OSPF_DEFAULT_DESTINATION 0x00000000 /* 0.0.0.0 */ +#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001U +#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffffU + +/* OSPF Interface Types */ +#define OSPF_IFTYPE_NONE 0 +#define OSPF_IFTYPE_POINTOPOINT 1 +#define OSPF_IFTYPE_BROADCAST 2 +#define OSPF_IFTYPE_NBMA 3 +#define OSPF_IFTYPE_POINTOMULTIPOINT 4 +#define OSPF_IFTYPE_VIRTUALLINK 5 +#define OSPF_IFTYPE_LOOPBACK 6 +#define OSPF_IFTYPE_MAX 7 + +/* OSPF interface default values. */ +#define OSPF_OUTPUT_COST_DEFAULT 10 +#define OSPF_OUTPUT_COST_INFINITE UINT16_MAX +#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 +#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 +#define OSPF_HELLO_INTERVAL_DEFAULT 10 +#define OSPF_ROUTER_PRIORITY_DEFAULT 1 +#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 +#define OSPF_TRANSMIT_DELAY_DEFAULT 1 +#define OSPF_DEFAULT_BANDWIDTH 10000 /* Kbps */ + +#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */ + +#define OSPF_POLL_INTERVAL_DEFAULT 60 +#define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0 + +#define OSPF_MTU_IGNORE_DEFAULT 0 +#define OSPF_FAST_HELLO_DEFAULT 0 + +#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ + +/* SPF Throttling timer values. */ +#define OSPF_SPF_DELAY_DEFAULT 200 +#define OSPF_SPF_HOLDTIME_DEFAULT 1000 +#define OSPF_SPF_MAX_HOLDTIME_DEFAULT 10000 + +#define OSPF_LSA_MAXAGE_CHECK_INTERVAL 30 +#define OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 + +#endif /* _LIBOSPFD_H */ diff --git a/lib/linklist.c b/lib/linklist.c index 485a80be..4b16f07d 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -23,7 +23,7 @@ #include "linklist.h" #include "memory.h" - + /* Allocate new list. */ struct list * list_new (void) @@ -51,7 +51,7 @@ listnode_free (struct listnode *node) { XFREE (MTYPE_LINK_NODE, node); } - + /* Add new data to the list. */ void listnode_add (struct list *list, void *val) @@ -159,6 +159,13 @@ listnode_add_after (struct list *list, struct listnode *pp, void *val) list->count++; } +/* Move given listnode to tail of the list */ +void +listnode_move_to_tail (struct list *l, struct listnode *n) +{ + LISTNODE_DETACH(l,n); + LISTNODE_ATTACH(l,n); +} /* Delete specific date pointer from the list. */ void @@ -242,7 +249,7 @@ listnode_lookup (struct list *list, void *data) return node; return NULL; } - + /* Delete the node from list. For ospfd and ospf6d. */ void list_delete_node (struct list *list, struct listnode *node) @@ -258,7 +265,7 @@ list_delete_node (struct list *list, struct listnode *node) list->count--; listnode_free (node); } - + /* ospf_spf.c */ void list_add_node_prev (struct list *list, struct listnode *current, void *val) diff --git a/lib/linklist.h b/lib/linklist.h index f0ae3625..6209c8b9 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -68,6 +68,7 @@ extern void list_free (struct list *); extern void listnode_add (struct list *, void *); extern void listnode_add_sort (struct list *, void *); extern void listnode_add_after (struct list *, struct listnode *, void *); +extern void listnode_move_to_tail (struct list *, struct listnode *); extern void listnode_delete (struct list *, void *); extern struct listnode *listnode_lookup (struct list *, void *); extern void *listnode_head (struct list *); @@ -90,7 +91,7 @@ extern void list_add_list (struct list *, struct list *); #define ALL_LIST_ELEMENTS(list,node,nextnode,data) \ (node) = listhead(list), ((data) = NULL); \ (node) != NULL && \ - ((data) = listgetdata(node),(nextnode) = listnextnode(node), 1); \ + ((data) = listgetdata(node),(nextnode) = node->next, 1); \ (node) = (nextnode), ((data) = NULL) /* read-only list iteration macro. @@ -112,6 +113,7 @@ extern void list_add_list (struct list *, struct list *); #define LISTNODE_ATTACH(L,N) \ do { \ (N)->prev = (L)->tail; \ + (N)->next = NULL; \ if ((L)->head == NULL) \ (L)->head = (N); \ else \ diff --git a/lib/log.c b/lib/log.c index e4ec7c22..0ceb0360 100644 --- a/lib/log.c +++ b/lib/log.c @@ -51,6 +51,7 @@ const char *zlog_proto_names[] = "BABEL", "OSPF6", "ISIS", + "PIM", "MASC", NULL, }; @@ -69,7 +70,7 @@ const char *zlog_priority[] = }; - + /* For time string format. */ size_t @@ -145,7 +146,7 @@ time_print(FILE *fp, struct timestamp_control *ctl) fprintf(fp, "%s ", ctl->buf); } - + /* va_list version of zlog. */ static void vzlog (struct zlog *zl, int priority, const char *format, va_list args) @@ -425,6 +426,40 @@ zlog_signal(int signo, const char *action NULL #endif ); + + s = buf; + if (!thread_current) + s = str_append (LOC, "no thread information available\n"); + else + { + s = str_append (LOC, "in thread "); + s = str_append (LOC, thread_current->funcname); + s = str_append (LOC, " scheduled from "); + s = str_append (LOC, thread_current->schedfrom); + s = str_append (LOC, ":"); + s = num_append (LOC, thread_current->schedfrom_line); + s = str_append (LOC, "\n"); + } + +#define DUMP(FD) write(FD, buf, s-buf); + /* If no file logging configured, try to write to fallback log file. */ + if (logfile_fd >= 0) + DUMP(logfile_fd) + if (!zlog_default) + DUMP(STDERR_FILENO) + else + { + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) + DUMP(STDOUT_FILENO) + /* Remove trailing '\n' for monitor and syslog */ + *--s = '\0'; + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) + vty_log_fixed(buf,s-buf); + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) + syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart); + } +#undef DUMP + #undef PRI #undef LOC } @@ -443,8 +478,8 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) #define LOC s,buf+sizeof(buf)-s #ifdef HAVE_GLIBC_BACKTRACE - if (((size = backtrace(array,array_size(array)) <= 0) || - ((size_t)size > array_size(array)))) + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) return; #define DUMP(FD) { \ @@ -526,8 +561,8 @@ zlog_backtrace(int priority) int size, i; char **strings; - if (((size = backtrace(array,array_size(array))) <= 0) || - ((size_t)size > array_size(array))) + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) { zlog_err("Cannot get backtrace, returned invalid # of frames %d " "(valid range is between 1 and %lu)", @@ -604,6 +639,16 @@ PLOG_FUNC(plog_debug, LOG_DEBUG) #undef PLOG_FUNC +void zlog_thread_info (int log_level) +{ + if (thread_current) + zlog(NULL, log_level, "Current thread function %s, scheduled from " + "file %s, line %u", thread_current->funcname, + thread_current->schedfrom, thread_current->schedfrom_line); + else + zlog(NULL, log_level, "Current thread not known/applicable"); +} + void _zlog_assert_failed (const char *assertion, const char *file, unsigned int line, const char *function) @@ -616,10 +661,11 @@ _zlog_assert_failed (const char *assertion, const char *file, zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", assertion,file,line,(function ? function : "?")); zlog_backtrace(LOG_CRIT); + zlog_thread_info(LOG_CRIT); abort(); } - + /* Open log stream */ struct zlog * openzlog (const char *progname, zlog_proto_t protocol, @@ -756,7 +802,7 @@ zlog_rotate (struct zlog *zl) return 1; } - + /* Message lookup function. */ const char * lookup (const struct message *mes, int key) @@ -925,13 +971,19 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_STATIC; else if (strncmp (s, "r", 1) == 0) return ZEBRA_ROUTE_RIP; - else if (strncmp (s, "o", 1) == 0) + else if (strncmp (s, "os", 2) == 0) return ZEBRA_ROUTE_OSPF; else if (strncmp (s, "i", 1) == 0) return ZEBRA_ROUTE_ISIS; else if (strncmp (s, "bg", 2) == 0) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ba", 2) == 0) + else if (strncmp (s, "h", 1) == 0) + return ZEBRA_ROUTE_HSLS; + else if (strncmp (s, "ol", 2) == 0) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; + else if (strncmp (s, "bab", 3) == 0) return ZEBRA_ROUTE_BABEL; } if (afi == AFI_IP6) @@ -944,13 +996,19 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_STATIC; else if (strncmp (s, "r", 1) == 0) return ZEBRA_ROUTE_RIPNG; - else if (strncmp (s, "o", 1) == 0) + else if (strncmp (s, "os", 2) == 0) return ZEBRA_ROUTE_OSPF6; else if (strncmp (s, "i", 1) == 0) return ZEBRA_ROUTE_ISIS; else if (strncmp (s, "bg", 2) == 0) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ba", 2) == 0) + else if (strncmp (s, "h", 1) == 0) + return ZEBRA_ROUTE_HSLS; + else if (strncmp (s, "ol", 2) == 0) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; + else if (strncmp (s, "bab", 3) == 0) return ZEBRA_ROUTE_BABEL; } return -1; diff --git a/lib/log.h b/lib/log.h index 27f21b31..77cd53bc 100644 --- a/lib/log.h +++ b/lib/log.h @@ -53,6 +53,7 @@ typedef enum ZLOG_BABEL, ZLOG_OSPF6, ZLOG_ISIS, + ZLOG_PIM, ZLOG_MASC } zlog_proto_t; @@ -132,6 +133,8 @@ extern void plog_notice (struct zlog *, const char *format, ...) extern void plog_debug (struct zlog *, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); +extern void zlog_thread_info (int log_level); + /* Set logging level for the given destination. If the log_level argument is ZLOG_DISABLED, then the destination is disabled. This function should not be used for file logging (use zlog_set_file @@ -146,8 +149,9 @@ extern int zlog_reset_file (struct zlog *zl); /* Rotate log. */ extern int zlog_rotate (struct zlog *); -/* For hackey massage lookup and check */ -#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)", #x) +/* For hackey message lookup and check */ +#define LOOKUP_DEF(x, y, def) mes_lookup(x, x ## _max, y, def, #x) +#define LOOKUP(x, y) LOOKUP_DEF(x, y, "(no item found)") extern const char *lookup (const struct message *, int); extern const char *mes_lookup (const struct message *meslist, diff --git a/lib/md5.c b/lib/md5.c index 2fc36e17..ce459bbe 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -306,7 +306,7 @@ unsigned char* text; /* pointer to data stream */ int text_len; /* length of data stream */ unsigned char* key; /* pointer to authentication key */ int key_len; /* length of authentication key */ -caddr_t digest; /* caller digest to be filled in */ +uint8_t * digest; /* caller digest to be filled in */ { MD5_CTX context; diff --git a/lib/md5.h b/lib/md5.h index a03bf22a..4e5ffbd9 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -84,6 +84,6 @@ do { \ /* From RFC 2104 */ void hmac_md5(unsigned char* text, int text_len, unsigned char* key, - int key_len, caddr_t digest); + int key_len, uint8_t *digest); #endif /* ! _LIBZEBRA_MD5_H_*/ diff --git a/lib/memory.c b/lib/memory.c index 684ebcff..84daeeef 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -32,7 +32,7 @@ static void alloc_inc (int); static void alloc_dec (int); static void log_memstats(int log_priority); - + static const struct message mstr [] = { { MTYPE_THREAD, "thread" }, @@ -42,7 +42,7 @@ static const struct message mstr [] = { MTYPE_IF, "interface" }, { 0, NULL }, }; - + /* Fatal memory allocation error occured. */ static void __attribute__ ((noreturn)) zerror (const char *fname, int type, size_t size) @@ -150,7 +150,7 @@ zstrdup (int type, const char *str) alloc_inc (type); return dup; } - + #ifdef MEMORY_LOG static struct { @@ -259,7 +259,7 @@ alloc_dec (int type) { mstat[type].alloc--; } - + /* Looking up memory status from vty interface. */ #include "vector.h" #include "vty.h" @@ -521,6 +521,17 @@ DEFUN (show_memory_isis, return CMD_SUCCESS; } +DEFUN (show_memory_pim, + show_memory_pim_cmd, + "show memory pim", + SHOW_STR + "Memory statistics\n" + "PIM memory\n") +{ + show_memory_vty (vty, memory_list_pim); + return CMD_SUCCESS; +} + void memory_init (void) { @@ -545,6 +556,7 @@ memory_init (void) install_element (VIEW_NODE, &show_memory_ospf_cmd); install_element (VIEW_NODE, &show_memory_ospf6_cmd); install_element (VIEW_NODE, &show_memory_isis_cmd); + install_element (VIEW_NODE, &show_memory_pim_cmd); install_element (ENABLE_NODE, &show_memory_cmd); install_element (ENABLE_NODE, &show_memory_all_cmd); @@ -557,8 +569,9 @@ memory_init (void) install_element (ENABLE_NODE, &show_memory_ospf_cmd); install_element (ENABLE_NODE, &show_memory_ospf6_cmd); install_element (ENABLE_NODE, &show_memory_isis_cmd); + install_element (ENABLE_NODE, &show_memory_pim_cmd); } - + /* Stats querying from users */ /* Return a pointer to a human friendly string describing * the byte count passed in. E.g: diff --git a/lib/memtypes.awk b/lib/memtypes.awk index 5429f6e8..bd13327d 100644 --- a/lib/memtypes.awk +++ b/lib/memtypes.awk @@ -1,4 +1,23 @@ -# $Id: memtypes.awk,v 1.4 2006/03/30 14:30:19 paul Exp $ +### +# Copyright (C) Paul Jakma 2005 +# +# This file is part of Quagga. +# +# Quagga 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. +# +# Quagga 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 General Public License +# along with Quagga; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +### # # Scan a file of memory definitions (see eg memtypes.c) and generate # a corresponding header file with an enum of the MTYPE's and declarations diff --git a/lib/memtypes.c b/lib/memtypes.c index bbf96929..1a0c11fe 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -54,7 +54,7 @@ struct memory_list memory_list_lib[] = { MTYPE_ROUTE_MAP_RULE, "Route map rule" }, { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" }, { MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" }, - { MTYPE_DESC, "Command desc" }, + { MTYPE_CMD_TOKENS, "Command desc" }, { MTYPE_KEY, "Key" }, { MTYPE_KEYCHAIN, "Key chain" }, { MTYPE_IF_RMAP, "Interface route map" }, @@ -82,6 +82,8 @@ struct memory_list memory_list_zebra[] = { MTYPE_RIB_QUEUE, "RIB process work queue" }, { MTYPE_STATIC_IPV4, "Static IPv4 route" }, { MTYPE_STATIC_IPV6, "Static IPv6 route" }, + { MTYPE_RIB_DEST, "RIB destination" }, + { MTYPE_RIB_TABLE_INFO, "RIB table info" }, { -1, NULL }, }; @@ -253,6 +255,21 @@ struct memory_list memory_list_isis[] = { -1, NULL }, }; +struct memory_list memory_list_pim[] = +{ + { MTYPE_PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL" }, + { MTYPE_PIM_INTERFACE, "PIM interface" }, + { MTYPE_PIM_IGMP_JOIN, "PIM interface IGMP static join" }, + { MTYPE_PIM_IGMP_SOCKET, "PIM interface IGMP socket" }, + { MTYPE_PIM_IGMP_GROUP, "PIM interface IGMP group" }, + { MTYPE_PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source" }, + { MTYPE_PIM_NEIGHBOR, "PIM interface neighbor" }, + { MTYPE_PIM_IFCHANNEL, "PIM interface (S,G) state" }, + { MTYPE_PIM_UPSTREAM, "PIM upstream (S,G) state" }, + { MTYPE_PIM_SSMPINGD, "PIM sspimgd socket" }, + { -1, NULL }, +}; + struct memory_list memory_list_vtysh[] = { { MTYPE_VTYSH_CONFIG, "Vtysh configuration", }, @@ -269,5 +286,6 @@ struct mlist mlists[] __attribute__ ((unused)) = { { memory_list_ospf6, "OSPF6" }, { memory_list_isis, "ISIS" }, { memory_list_bgp, "BGP" }, + { memory_list_pim, "PIM" }, { NULL, NULL}, }; diff --git a/lib/plist.c b/lib/plist.c index 0f802a83..61075560 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -110,7 +110,7 @@ static struct prefix_master prefix_master_orf = NULL, NULL, }; - + static struct prefix_master * prefix_master_get (afi_t afi) { @@ -621,7 +621,7 @@ prefix_list_print (struct prefix_list *plist) } } } - + /* Retrun 1 when plist already include pentry policy. */ static struct prefix_list_entry * prefix_entry_dup_check (struct prefix_list *plist, @@ -733,16 +733,16 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, #endif /* HAVE_IPV6 */ /* ge and le check. */ - if (genum && genum <= p.prefixlen) + if (genum && (genum <= p.prefixlen)) return vty_invalid_prefix_range (vty, prefix); - if (lenum && lenum <= p.prefixlen) + if (lenum && (lenum <= p.prefixlen)) return vty_invalid_prefix_range (vty, prefix); - if (lenum && genum > lenum) + if (lenum && (genum > lenum)) return vty_invalid_prefix_range (vty, prefix); - if (genum && lenum == (afi == AFI_IP ? 32 : 128)) + if (genum && (lenum == (afi == AFI_IP ? 32 : 128))) lenum = 0; /* Get prefix_list with name. */ @@ -1165,7 +1165,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, } return CMD_SUCCESS; } - + DEFUN (ip_prefix_list, ip_prefix_list_cmd, "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", @@ -1759,7 +1759,7 @@ DEFUN (clear_ip_prefix_list_name_prefix, { return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]); } - + #ifdef HAVE_IPV6 DEFUN (ipv6_prefix_list, ipv6_prefix_list_cmd, @@ -2355,7 +2355,7 @@ DEFUN (clear_ipv6_prefix_list_name_prefix, return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]); } #endif /* HAVE_IPV6 */ - + /* Configuration write function. */ static int config_write_prefix_afi (afi_t afi, struct vty *vty) diff --git a/lib/pqueue.c b/lib/pqueue.c index 12a779f2..69ab8e65 100644 --- a/lib/pqueue.c +++ b/lib/pqueue.c @@ -168,3 +168,20 @@ pqueue_dequeue (struct pqueue *queue) trickle_down (0, queue); return data; } + +void +pqueue_remove_at (int index, struct pqueue *queue) +{ + queue->array[index] = queue->array[--queue->size]; + + if (index > 0 + && (*queue->cmp) (queue->array[index], + queue->array[PARENT_OF(index)]) < 0) + { + trickle_up (index, queue); + } + else + { + trickle_down (index, queue); + } +} diff --git a/lib/pqueue.h b/lib/pqueue.h index be37f98d..8bb6961d 100644 --- a/lib/pqueue.h +++ b/lib/pqueue.h @@ -38,6 +38,7 @@ extern void pqueue_delete (struct pqueue *queue); extern void pqueue_enqueue (void *data, struct pqueue *queue); extern void *pqueue_dequeue (struct pqueue *queue); +extern void pqueue_remove_at (int index, struct pqueue *queue); extern void trickle_down (int index, struct pqueue *queue); extern void trickle_up (int index, struct pqueue *queue); diff --git a/lib/prefix.c b/lib/prefix.c index a3b1adf8..dbfdc830 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -27,7 +27,7 @@ #include "sockunion.h" #include "memory.h" #include "log.h" - + /* Maskbit. */ static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; @@ -186,7 +186,7 @@ prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen) { return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen); } - + /* Address Famiy Identifier to Address Family converter. */ int afi2family (afi_t afi) @@ -494,7 +494,7 @@ prefix_ipv4_any (const struct prefix_ipv4 *p) { return (p->prefix.s_addr == 0 && p->prefixlen == 0); } - + #ifdef HAVE_IPV6 /* Allocate a new ip version 6 route */ diff --git a/lib/prefix.h b/lib/prefix.h index 7f0d3607..45889e08 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -52,6 +52,7 @@ struct prefix struct in_addr adv_router; } lp; u_char val[8]; + uintptr_t ptr; } u __attribute__ ((aligned (8))); }; @@ -89,6 +90,33 @@ struct prefix_rd u_char val[8] __attribute__ ((aligned (8))); }; +/* Prefix for a generic pointer */ +struct prefix_ptr +{ + u_char family; + u_char prefixlen; + uintptr_t prefix __attribute__ ((aligned (8))); +}; + +/* helper to get type safety/avoid casts on calls + * (w/o this, functions accepting all prefix types need casts on the caller + * side, which strips type safety since the cast will accept any pointer + * type.) + */ +union prefix46ptr +{ + struct prefix *p; + struct prefix_ipv4 *p4; + struct prefix_ipv6 *p6; +} __attribute__ ((transparent_union)); + +union prefix46constptr +{ + const struct prefix *p; + const struct prefix_ipv4 *p4; + const struct prefix_ipv6 *p6; +} __attribute__ ((transparent_union)); + #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /* INET_ADDRSTRLEN */ @@ -196,4 +224,14 @@ extern const char *inet6_ntoa (struct in6_addr); extern int all_digit (const char *); +static inline int ipv4_martian (struct in_addr *addr) +{ + in_addr_t ip = addr->s_addr; + + if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_DE(ip)) { + return 1; + } + return 0; +} + #endif /* _ZEBRA_PREFIX_H */ diff --git a/lib/privs.c b/lib/privs.c index 69606f57..e182543a 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -47,7 +47,7 @@ struct _pset { typedef cap_value_t pvalue_t; typedef struct _pset pset_t; typedef cap_t pstorage_t; - + #elif defined (HAVE_SOLARIS_CAPABILITIES) typedef priv_t pvalue_t; typedef priv_set_t pset_t; @@ -56,7 +56,7 @@ typedef priv_set_t *pstorage_t; #error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!" #endif /* HAVE_LCAPS */ #endif /* HAVE_CAPABILITIES */ - + /* the default NULL state we report is RAISED, but could be LOWERED if * zprivs_terminate is called and the NULL handler is installed. */ @@ -139,7 +139,7 @@ static struct [ZCAP_FOWNER] = { 1, (pvalue_t []) { PRIV_FILE_OWNER }, }, #endif /* HAVE_SOLARIS_CAPABILITIES */ }; - + #ifdef HAVE_LCAPS /* Linux forms of capabilities methods */ /* convert zebras privileges to system capabilities */ @@ -339,7 +339,7 @@ zprivs_caps_terminate (void) cap_free (zprivs_state.caps); } #elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ - + /* Solaris specific capability/privilege methods * * Resources: @@ -556,7 +556,7 @@ zprivs_caps_terminate (void) #error "Neither Solaris nor Linux capabilities, dazed and confused..." #endif /* HAVE_LCAPS */ #endif /* HAVE_CAPABILITIES */ - + int zprivs_change_uid (zebra_privs_ops_t op) { diff --git a/lib/queue.h b/lib/queue.h new file mode 100644 index 00000000..48b363e2 --- /dev/null +++ b/lib/queue.h @@ -0,0 +1,635 @@ +/*- + * 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. + * 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail 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 singly-linked 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 + * 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, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO 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 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. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#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 functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (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)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#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 functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (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)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#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 */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#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)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/lib/regex-gnu.h b/lib/regex-gnu.h index d88ab92b..4cee464f 100644 --- a/lib/regex-gnu.h +++ b/lib/regex-gnu.h @@ -165,7 +165,7 @@ typedef unsigned long int reg_syntax_t; stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; - + /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so don't delete them!) */ @@ -234,7 +234,7 @@ extern reg_syntax_t re_syntax_options; | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* [[[end syntaxes]]] */ - + /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ @@ -309,7 +309,7 @@ typedef enum REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; - + /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields `buffer', `allocated', `fastmap', `translate', and `no_sub' can be set. After the pattern has been @@ -389,7 +389,7 @@ struct re_pattern_buffer }; typedef struct re_pattern_buffer regex_t; - + /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; @@ -420,7 +420,7 @@ typedef struct regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; - + /* Declarations for routines. */ /* To avoid duplicating every routine declaration -- once with a @@ -532,7 +532,7 @@ extern void regfree _RE_ARGS ((regex_t *__preg)); #endif /* C++ */ #endif /* regex.h */ - + /* Local variables: make-backup-files: t diff --git a/lib/regex.c b/lib/regex.c index a22e03f6..122f4476 100644 --- a/lib/regex.c +++ b/lib/regex.c @@ -209,7 +209,7 @@ init_syntax_once () # define SYNTAX(c) re_syntax_table[c] #endif /* not emacs */ - + /* Get the interface, including the syntax bits. */ #include @@ -279,7 +279,7 @@ init_syntax_once () /* As in Harbison and Steele. */ # define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) #endif - + /* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we use `alloca' instead of `malloc'. This is because using malloc in re_search* or re_match* could cause memory leaks when C-g is used in @@ -388,7 +388,7 @@ static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp, int pos, struct re_registers *regs, int stop)); - + /* These are the command codes that appear in compiled regular expressions. Some opcodes are followed by argument bytes. A command code can specify any interpretation whatsoever for its @@ -527,7 +527,7 @@ typedef enum notsyntaxspec #endif /* emacs */ } re_opcode_t; - + /* Common operations on the compiled pattern. */ /* Store NUMBER in two contiguous bytes starting at DESTINATION. */ @@ -604,7 +604,7 @@ extract_number_and_incr (destination, source) # endif /* not EXTRACT_MACROS */ #endif /* DEBUG */ - + /* If DEBUG is defined, Regex prints many voluminous messages about what it is doing (if the variable `debug' is nonzero). If linked with the main program in `iregex.c', you can enter patterns and strings @@ -977,7 +977,7 @@ printchar (c) # define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) #endif /* not DEBUG */ - + /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can also be assigned to arbitrarily: each pattern buffer stores its own syntax, so it can be changed between regex compilations. */ @@ -1011,7 +1011,7 @@ re_set_syntax (syntax) #ifdef _LIBC weak_alias (__re_set_syntax, re_set_syntax) #endif - + /* This table gives an error message for each of the error codes listed in regex.h. Obviously the order here has to be same as there. POSIX doesn't require that we do anything for REG_NOERROR, @@ -1091,7 +1091,7 @@ static const size_t re_error_msgid_idx[] = REG_ESIZE_IDX, REG_ERPAREN_IDX }; - + /* Avoiding alloca during matching, to placate r_alloc. */ /* Define MATCH_MAY_ALLOCATE unless we need to make sure that the @@ -1129,7 +1129,7 @@ static const size_t re_error_msgid_idx[] = # undef MATCH_MAY_ALLOCATE #endif - + /* Failure stack declarations and macros; both re_compile_fastmap and re_match_2 use a failure stack. These have to be macros because of REGEX_ALLOCATE_STACK. */ @@ -1495,7 +1495,7 @@ typedef struct } /* POP_FAILURE_POINT */ - + /* Structure for per-register (a.k.a. per-group) information. Other register information, such as the starting and ending positions (which are addresses), and the list of @@ -1555,7 +1555,7 @@ typedef union static char reg_unset_dummy; #define REG_UNSET_VALUE (®_unset_dummy) #define REG_UNSET(e) ((e) == REG_UNSET_VALUE) - + /* Subroutine declarations and macros for regex_compile. */ static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size, @@ -1809,7 +1809,7 @@ typedef struct || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) #endif - + #ifndef MATCH_MAY_ALLOCATE /* If we cannot allocate large objects within re_match_2_internal, @@ -1857,7 +1857,7 @@ regex_grow_registers (num_regs) } #endif /* not MATCH_MAY_ALLOCATE */ - + static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type compile_stack, regnum_t regnum)); @@ -2991,7 +2991,7 @@ regex_compile (pattern, size, syntax, bufp) return REG_NOERROR; } /* regex_compile */ - + /* Subroutines for `regex_compile'. */ /* Store OP at LOC followed by two-byte integer parameter ARG. */ @@ -3178,7 +3178,7 @@ compile_range (p_ptr, pend, translate, syntax, b) return REG_NOERROR; } - + /* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible characters can start a string that matches the pattern. This fastmap @@ -3484,7 +3484,7 @@ re_compile_fastmap (bufp) #ifdef _LIBC weak_alias (__re_compile_fastmap, re_compile_fastmap) #endif - + /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use this memory for recording register information. STARTS and ENDS @@ -3522,7 +3522,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends) #ifdef _LIBC weak_alias (__re_set_registers, re_set_registers) #endif - + /* Searching routines. */ /* Like re_search_2, below, but only one string is specified, and @@ -3704,7 +3704,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) #ifdef _LIBC weak_alias (__re_search_2, re_search_2) #endif - + /* This converts PTR, a pointer into one of the search strings `string1' and `string2' into an offset from the beginning of that string. */ #define POINTER_TO_OFFSET(ptr) \ @@ -3783,7 +3783,7 @@ weak_alias (__re_search_2, re_search_2) to actually save any registers when none are active. */ #define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) #define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) - + /* Matching routines. */ #ifndef emacs /* Emacs never uses this. */ @@ -5248,7 +5248,7 @@ re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) return -1; /* Failure to match. */ } /* re_match_2 */ - + /* Subroutine definitions for re_match_2. */ @@ -5511,7 +5511,7 @@ bcmp_translate (s1, s2, len, translate) } return 0; } - + /* Entry points for GNU code. */ /* re_compile_pattern is the GNU regular expression compiler: it @@ -5552,7 +5552,7 @@ re_compile_pattern (pattern, length, bufp) #ifdef _LIBC weak_alias (__re_compile_pattern, re_compile_pattern) #endif - + /* Entry points compatible with 4.2 BSD regex library. We don't define them unless specifically requested. */ @@ -5623,7 +5623,7 @@ re_exec (s) } #endif /* _REGEX_RE_COMP */ - + /* POSIX.2 functions. Don't define these for Emacs. */ #ifndef emacs diff --git a/lib/route_types.txt b/lib/route_types.txt index cebf01fc..2dfd1453 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -51,14 +51,11 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF" ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" -# HSLS and OLSR both are AFI independent (so: 1, 1), however -# we want to disable for them for general Quagga distribution. -# This at least makes it trivial for users of these protocols -# to 'switch on' redist support (direct numeric entry remaining -# possible). -ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" -ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" +ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 1, 1, "HSLS" +ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR" +ZEBRA_ROUTE_BATMAN, batman, batmand,'b', 1, 1, "BATMAN" ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" +ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" ## help strings ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" @@ -71,6 +68,8 @@ ZEBRA_ROUTE_OSPF, "Open Shortest Path First (OSPFv2)" ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)" ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" +ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" -ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" +ZEBRA_ROUTE_OLSR, "Optimized Link State Routing (OLSR)" +ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" diff --git a/lib/routemap.c b/lib/routemap.c index 4f4e6d62..1e1510eb 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -28,7 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "vty.h" #include "log.h" - + /* Vector for route match rules. */ static vector route_match_vec; @@ -72,7 +72,7 @@ route_map_rule_delete (struct route_map_rule_list *, static void route_map_index_delete (struct route_map_index *, int); - + /* New route map allocation. Please note route map's name must be specified. */ static struct route_map * @@ -420,7 +420,7 @@ route_map_rule_new (void) new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule)); return new; } - + /* Install rule command to the match list. */ void route_map_install_match (struct route_map_rule_cmd *cmd) @@ -898,7 +898,7 @@ route_map_finish (void) vector_free (route_set_vec); route_set_vec = NULL; } - + /* VTY related functions. */ DEFUN (route_map, route_map_cmd, diff --git a/lib/sigevent.c b/lib/sigevent.c index 7d08fd97..c80a7290 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -175,11 +175,35 @@ program_counter(void *context) { #ifdef HAVE_UCONTEXT_H #ifdef GNU_LINUX -#ifdef REG_EIP - if (context) - return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP]); -#endif /* REG_EIP */ + /* these are from GNU libc, rather than Linux, strictly speaking */ +# if defined(REG_EIP) +# define REG_INDEX REG_EIP +# elif defined(REG_RIP) +# define REG_INDEX REG_RIP +# elif defined(__powerpc__) +# define REG_INDEX 32 +# endif +#elif defined(SUNOS_5) /* !GNU_LINUX */ +# define REG_INDEX REG_PC #endif /* GNU_LINUX */ + +#ifdef REG_INDEX +# ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS +# define REGS gregs[REG_INDEX] +# elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS) +# define REGS uc_regs->gregs[REG_INDEX] +# endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */ +#endif /* REG_INDEX */ + +#ifdef REGS + if (context) + return (void *)(((ucontext_t *)context)->uc_mcontext.REGS); +#elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP) + /* older Linux / struct pt_regs ? */ + if (context) + return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip); +#endif /* REGS */ + #endif /* HAVE_UCONTEXT_H */ return NULL; } diff --git a/lib/smux.c b/lib/smux.c index 07466400..70be4928 100644 --- a/lib/smux.c +++ b/lib/smux.c @@ -78,7 +78,7 @@ struct subtree enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ}; void smux_event (enum smux_event, int); - + /* SMUX socket. */ int smux_sock = -1; @@ -114,7 +114,7 @@ static struct cmd_node smux_node = /* thread master */ static struct thread_master *master; - + static int oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) { @@ -132,7 +132,7 @@ oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) return 0; } - + static void smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len) { @@ -1230,7 +1230,7 @@ smux_stop (void) smux_sock = -1; } } - + void @@ -1251,7 +1251,7 @@ smux_event (enum smux_event event, int sock) break; } } - + static int smux_str2oid (const char *str, oid *oid, size_t *oid_len) { diff --git a/lib/sockunion.c b/lib/sockunion.c index 5dcf7256..1a355d3b 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -304,13 +304,8 @@ sockunion_connect (int fd, union sockunion *peersu, unsigned short port, { #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID /* su.sin6.sin6_scope_id = ifindex; */ -#ifdef MUSICA - su.sin6.sin6_scope_id = ifindex; -#endif #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ -#ifndef MUSICA SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); -#endif } #endif /* KAME */ break; @@ -394,7 +389,7 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, #endif /* SIN6_LEN */ if (su_addr == NULL) { -#if defined(LINUX_IPV6) || defined(NRL) +#ifdef LINUX_IPV6 memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr)); #else su->sin6.sin6_addr = in6addr_any; diff --git a/lib/str.c b/lib/str.c index 4ab71e19..d8f039a0 100644 --- a/lib/str.c +++ b/lib/str.c @@ -16,6 +16,24 @@ Copyright (C) 1996, 1997, 1998, 2001, 2002 Free Software Foundation, Inc. */ +/* + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ #include diff --git a/lib/stream.c b/lib/stream.c index ee2920e6..c6f20c85 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -154,6 +154,25 @@ stream_dup (struct stream *s) return (stream_copy (new, s)); } +struct stream * +stream_dupcat (struct stream *s1, struct stream *s2, size_t offset) +{ + struct stream *new; + + STREAM_VERIFY_SANE (s1); + STREAM_VERIFY_SANE (s2); + + if ( (new = stream_new (s1->endp + s2->endp)) == NULL) + return NULL; + + memcpy (new->data, s1->data, offset); + memcpy (new->data + offset, s2->data, s2->endp); + memcpy (new->data + offset + s2->endp, s1->data + offset, + (s1->endp - offset)); + new->endp = s1->endp + s2->endp; + return new; +} + size_t stream_resize (struct stream *s, size_t newsize) { @@ -177,7 +196,7 @@ stream_resize (struct stream *s, size_t newsize) return s->size; } - + size_t stream_get_getp (struct stream *s) { @@ -266,7 +285,7 @@ stream_forward_endp (struct stream *s, size_t size) s->endp += size; } - + /* Copy from stream to destination. */ void stream_get (void *dst, struct stream *s, size_t size) @@ -473,7 +492,7 @@ stream_get_ipv4 (struct stream *s) return l; } - + /* Copy to source to stream. * * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap @@ -700,19 +719,19 @@ stream_put_prefix (struct stream *s, struct prefix *p) psize = PSIZE (p->prefixlen); - if (STREAM_WRITEABLE (s) < psize) + if (STREAM_WRITEABLE (s) < (psize + sizeof (u_char))) { STREAM_BOUND_WARN (s, "put"); return 0; } - stream_putc (s, p->prefixlen); + s->data[s->endp++] = p->prefixlen; memcpy (s->data + s->endp, &p->u.prefix, psize); s->endp += psize; return psize; } - + /* Read size from fd. */ int stream_read (struct stream *s, int fd, size_t size) @@ -735,32 +754,6 @@ stream_read (struct stream *s, int fd, size_t size) return nbytes; } -/* Read size from fd. */ -int -stream_read_unblock (struct stream *s, int fd, size_t size) -{ - int nbytes; - int val; - - STREAM_VERIFY_SANE(s); - - if (STREAM_WRITEABLE (s) < size) - { - STREAM_BOUND_WARN (s, "put"); - return 0; - } - - val = fcntl (fd, F_GETFL, 0); - fcntl (fd, F_SETFL, val|O_NONBLOCK); - nbytes = read (fd, s->data + s->endp, size); - fcntl (fd, F_SETFL, val); - - if (nbytes > 0) - s->endp += nbytes; - - return nbytes; -} - ssize_t stream_read_try(struct stream *s, int fd, size_t size) { @@ -918,7 +911,7 @@ stream_flush (struct stream *s, int fd) return nbytes; } - + /* Stream first in first out queue. */ struct stream_fifo * diff --git a/lib/stream.h b/lib/stream.h index f10aa6d4..1fc382d6 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -122,6 +122,9 @@ struct stream_fifo /* number of bytes still to be read */ #define STREAM_READABLE(S) ((S)->endp - (S)->getp) +#define STREAM_CONCAT_REMAIN(S1, S2, size) \ + ((size) - (S1)->endp - (S2)->endp) + /* deprecated macros - do not use in new code */ #define STREAM_PNT(S) stream_pnt((S)) #define STREAM_DATA(S) ((S)->data) @@ -145,6 +148,14 @@ extern size_t stream_get_endp (struct stream *); extern size_t stream_get_size (struct stream *); extern u_char *stream_get_data (struct stream *); +/** + * Create a new stream structure; copy offset bytes from s1 to the new + * stream; copy s2 data to the new stream; copy rest of s1 data to the + * new stream. + */ +extern struct stream *stream_dupcat(struct stream *s1, struct stream *s2, + size_t offset); + extern void stream_set_getp (struct stream *, size_t); extern void stream_set_endp (struct stream *, size_t); extern void stream_forward_getp (struct stream *, size_t); @@ -182,10 +193,6 @@ extern u_int32_t stream_get_ipv4 (struct stream *); Use stream_read_try instead. */ extern int stream_read (struct stream *, int, size_t); -/* Deprecated: all file descriptors should already be non-blocking. - Will be removed. Use stream_read_try instead. */ -extern int stream_read_unblock (struct stream *, int, size_t); - /* Read up to size bytes into the stream. Return code: >0: number of bytes read diff --git a/lib/table.c b/lib/table.c index 19b5d1b1..bd7023c1 100644 --- a/lib/table.c +++ b/lib/table.c @@ -29,7 +29,7 @@ static void route_node_delete (struct route_node *); static void route_table_free (struct route_table *); - + /* * route_table_init_with_delegate @@ -626,11 +626,8 @@ route_table_get_next_internal (const struct route_table *table, struct prefix *p) { struct route_node *node, *tmp_node; - u_char prefixlen; int cmp; - prefixlen = p->prefixlen; - node = table->top; while (node) diff --git a/lib/thread.c b/lib/thread.c index 16c92c24..cb513323 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -27,6 +27,7 @@ #include "memory.h" #include "log.h" #include "hash.h" +#include "pqueue.h" #include "command.h" #include "sigevent.h" @@ -44,7 +45,7 @@ extern int agentx_enabled; #include #endif - + /* Recent absolute time of day */ struct timeval recent_time; static struct timeval last_recent_time; @@ -53,9 +54,9 @@ static struct timeval relative_time; static struct timeval relative_time_base; /* init flag */ static unsigned short timers_inited; - + static struct hash *cpu_record = NULL; - + /* Struct timeval's tv_usec one second value. */ #define TIMER_SECOND_MICRO 1000000L @@ -101,13 +102,13 @@ timeval_cmp (struct timeval a, struct timeval b) ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); } -static unsigned long +unsigned long timeval_elapsed (struct timeval a, struct timeval b) { return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + (a.tv_usec - b.tv_usec)); } - + #if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__) static void quagga_gettimeofday_relative_adjust (void) @@ -246,7 +247,7 @@ recent_relative_time (void) { return relative_time; } - + static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -266,7 +267,7 @@ cpu_record_hash_alloc (struct cpu_thread_history *a) struct cpu_thread_history *new; new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history)); new->func = a->func; - strcpy(new->funcname, a->funcname); + new->funcname = a->funcname; return new; } @@ -333,7 +334,7 @@ cpu_record_print(struct vty *vty, thread_type filter) void *args[3] = {&tmp, vty, &filter}; memset(&tmp, 0, sizeof tmp); - strcpy(tmp.funcname, "TOTAL"); + tmp.funcname = "TOTAL"; tmp.types = filter; #ifdef HAVE_RUSAGE @@ -495,47 +496,50 @@ DEFUN(clear_thread_cpu, cpu_record_clear (filter); return CMD_SUCCESS; } - -/* List allocation and head/tail print out. */ -static void -thread_list_debug (struct thread_list *list) + +static int +thread_timer_cmp(void *a, void *b) { - printf ("count [%d] head [%p] tail [%p]\n", - list->count, list->head, list->tail); + struct thread *thread_a = a; + struct thread *thread_b = b; + + long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands); + + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; } -/* Debug print for thread_master. */ -static void __attribute__ ((unused)) -thread_master_debug (struct thread_master *m) +static void +thread_timer_update(void *node, int actual_position) { - printf ("-----------\n"); - printf ("readlist : "); - thread_list_debug (&m->read); - printf ("writelist : "); - thread_list_debug (&m->write); - printf ("timerlist : "); - thread_list_debug (&m->timer); - printf ("eventlist : "); - thread_list_debug (&m->event); - printf ("unuselist : "); - thread_list_debug (&m->unuse); - printf ("bgndlist : "); - thread_list_debug (&m->background); - printf ("total alloc: [%ld]\n", m->alloc); - printf ("-----------\n"); + struct thread *thread = node; + + thread->index = actual_position; } - + /* Allocate new thread master. */ struct thread_master * thread_master_create () { + struct thread_master *rv; + if (cpu_record == NULL) cpu_record - = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *))cpu_record_hash_cmp); - - return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER, - sizeof (struct thread_master)); + = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *))cpu_record_hash_cmp); + + rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); + + /* Initialize the timer queues */ + rv->timer = pqueue_create(); + rv->background = pqueue_create(); + rv->timer->cmp = rv->background->cmp = thread_timer_cmp; + rv->timer->update = rv->background->update = thread_timer_update; + + return rv; } /* Add a new thread to the list. */ @@ -552,22 +556,6 @@ thread_list_add (struct thread_list *list, struct thread *thread) list->count++; } -/* Add a new thread just before the point. */ -static void -thread_list_add_before (struct thread_list *list, - struct thread *point, - struct thread *thread) -{ - thread->next = point; - thread->prev = point->prev; - if (point->prev) - point->prev->next = thread; - else - list->head = thread; - point->prev = thread; - list->count++; -} - /* Delete a thread from the list. */ static struct thread * thread_list_delete (struct thread_list *list, struct thread *thread) @@ -594,7 +582,6 @@ thread_add_unuse (struct thread_master *m, struct thread *thread) assert (thread->prev == NULL); assert (thread->type == THREAD_UNUSED); thread_list_add (&m->unuse, thread); - /* XXX: Should we deallocate funcname here? */ } /* Free all unused thread. */ @@ -613,17 +600,29 @@ thread_list_free (struct thread_master *m, struct thread_list *list) } } +static void +thread_queue_free (struct thread_master *m, struct pqueue *queue) +{ + int i; + + for (i = 0; i < queue->size; i++) + XFREE(MTYPE_THREAD, queue->array[i]); + + m->alloc -= queue->size; + pqueue_delete(queue); +} + /* Stop thread scheduler. */ void thread_master_free (struct thread_master *m) { thread_list_free (m, &m->read); thread_list_free (m, &m->write); - thread_list_free (m, &m->timer); + thread_queue_free (m, m->timer); thread_list_free (m, &m->event); thread_list_free (m, &m->ready); thread_list_free (m, &m->unuse); - thread_list_free (m, &m->background); + thread_queue_free (m, m->background); XFREE (MTYPE_THREAD_MASTER, m); @@ -663,35 +662,13 @@ thread_timer_remain_second (struct thread *thread) return 0; } -/* Trim blankspace and "()"s */ -void -strip_funcname (char *dest, const char *funcname) -{ - char buff[FUNCNAME_LEN]; - char tmp, *e, *b = buff; - - strncpy(buff, funcname, sizeof(buff)); - buff[ sizeof(buff) -1] = '\0'; - e = buff +strlen(buff) -1; - - /* Wont work for funcname == "Word (explanation)" */ - - while (*b == ' ' || *b == '(') - ++b; - while (*e == ' ' || *e == ')') - --e; - e++; - - tmp = *e; - *e = '\0'; - strcpy (dest, b); - *e = tmp; -} +#define debugargdef const char *funcname, const char *schedfrom, int fromln +#define debugargpass funcname, schedfrom, fromln /* Get new thread. */ static struct thread * thread_get (struct thread_master *m, u_char type, - int (*func) (struct thread *), void *arg, const char* funcname) + int (*func) (struct thread *), void *arg, debugargdef) { struct thread *thread = thread_trim_head (&m->unuse); @@ -705,8 +682,11 @@ thread_get (struct thread_master *m, u_char type, thread->master = m; thread->func = func; thread->arg = arg; - - strip_funcname (thread->funcname, funcname); + thread->index = -1; + + thread->funcname = funcname; + thread->schedfrom = schedfrom; + thread->schedfrom_line = fromln; return thread; } @@ -714,7 +694,8 @@ thread_get (struct thread_master *m, u_char type, /* Add new read thread. */ struct thread * funcname_thread_add_read (struct thread_master *m, - int (*func) (struct thread *), void *arg, int fd, const char* funcname) + int (*func) (struct thread *), void *arg, int fd, + debugargdef) { struct thread *thread; @@ -726,7 +707,7 @@ funcname_thread_add_read (struct thread_master *m, return NULL; } - thread = thread_get (m, THREAD_READ, func, arg, funcname); + thread = thread_get (m, THREAD_READ, func, arg, debugargpass); FD_SET (fd, &m->readfd); thread->u.fd = fd; thread_list_add (&m->read, thread); @@ -737,7 +718,8 @@ funcname_thread_add_read (struct thread_master *m, /* Add new write thread. */ struct thread * funcname_thread_add_write (struct thread_master *m, - int (*func) (struct thread *), void *arg, int fd, const char* funcname) + int (*func) (struct thread *), void *arg, int fd, + debugargdef) { struct thread *thread; @@ -749,7 +731,7 @@ funcname_thread_add_write (struct thread_master *m, return NULL; } - thread = thread_get (m, THREAD_WRITE, func, arg, funcname); + thread = thread_get (m, THREAD_WRITE, func, arg, debugargpass); FD_SET (fd, &m->writefd); thread->u.fd = fd; thread_list_add (&m->write, thread); @@ -762,21 +744,20 @@ funcname_thread_add_timer_timeval (struct thread_master *m, int (*func) (struct thread *), int type, void *arg, - struct timeval *time_relative, - const char* funcname) + struct timeval *time_relative, + debugargdef) { struct thread *thread; - struct thread_list *list; + struct pqueue *queue; struct timeval alarm_time; - struct thread *tt; assert (m != NULL); assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); assert (time_relative); - list = ((type == THREAD_TIMER) ? &m->timer : &m->background); - thread = thread_get (m, type, func, arg, funcname); + queue = ((type == THREAD_TIMER) ? m->timer : m->background); + thread = thread_get (m, type, func, arg, debugargpass); /* Do we need jitter here? */ quagga_get_relative (NULL); @@ -784,16 +765,7 @@ funcname_thread_add_timer_timeval (struct thread_master *m, alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; thread->u.sands = timeval_adjust(alarm_time); - /* Sort by timeval. */ - for (tt = list->head; tt; tt = tt->next) - if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0) - break; - - if (tt) - thread_list_add_before (list, tt, thread); - else - thread_list_add (list, thread); - + pqueue_enqueue(thread, queue); return thread; } @@ -802,7 +774,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m, struct thread * funcname_thread_add_timer (struct thread_master *m, int (*func) (struct thread *), - void *arg, long timer, const char* funcname) + void *arg, long timer, + debugargdef) { struct timeval trel; @@ -812,14 +785,15 @@ funcname_thread_add_timer (struct thread_master *m, trel.tv_usec = 0; return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, - &trel, funcname); + &trel, debugargpass); } /* Add timer event thread with "millisecond" resolution */ struct thread * funcname_thread_add_timer_msec (struct thread_master *m, int (*func) (struct thread *), - void *arg, long timer, const char* funcname) + void *arg, long timer, + debugargdef) { struct timeval trel; @@ -829,15 +803,15 @@ funcname_thread_add_timer_msec (struct thread_master *m, trel.tv_usec = 1000*(timer % 1000); return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, - arg, &trel, funcname); + arg, &trel, debugargpass); } /* Add a background thread, with an optional millisec delay */ struct thread * funcname_thread_add_background (struct thread_master *m, int (*func) (struct thread *), - void *arg, long delay, - const char *funcname) + void *arg, long delay, + debugargdef) { struct timeval trel; @@ -855,19 +829,20 @@ funcname_thread_add_background (struct thread_master *m, } return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND, - arg, &trel, funcname); + arg, &trel, debugargpass); } /* Add simple event thread. */ struct thread * funcname_thread_add_event (struct thread_master *m, - int (*func) (struct thread *), void *arg, int val, const char* funcname) + int (*func) (struct thread *), void *arg, int val, + debugargdef) { struct thread *thread; assert (m != NULL); - thread = thread_get (m, THREAD_EVENT, func, arg, funcname); + thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); thread->u.val = val; thread_list_add (&m->event, thread); @@ -878,7 +853,8 @@ funcname_thread_add_event (struct thread_master *m, void thread_cancel (struct thread *thread) { - struct thread_list *list; + struct thread_list *list = NULL; + struct pqueue *queue = NULL; switch (thread->type) { @@ -893,7 +869,7 @@ thread_cancel (struct thread *thread) list = &thread->master->write; break; case THREAD_TIMER: - list = &thread->master->timer; + queue = thread->master->timer; break; case THREAD_EVENT: list = &thread->master->event; @@ -902,13 +878,28 @@ thread_cancel (struct thread *thread) list = &thread->master->ready; break; case THREAD_BACKGROUND: - list = &thread->master->background; + queue = thread->master->background; break; default: return; break; } - thread_list_delete (list, thread); + + if (queue) + { + assert(thread->index >= 0); + assert(thread == queue->array[thread->index]); + pqueue_remove_at(thread->index, queue); + } + else if (list) + { + thread_list_delete (list, thread); + } + else + { + assert(!"Thread should be either in queue or list!"); + } + thread->type = THREAD_UNUSED; thread_add_unuse (thread->master, thread); } @@ -958,11 +949,12 @@ thread_cancel_event (struct thread_master *m, void *arg) } static struct timeval * -thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val) +thread_timer_wait (struct pqueue *queue, struct timeval *timer_val) { - if (!thread_empty (tlist)) + if (queue->size) { - *timer_val = timeval_subtract (tlist->head->u.sands, relative_time); + struct thread *next_timer = queue->array[0]; + *timer_val = timeval_subtract (next_timer->u.sands, relative_time); return timer_val; } return NULL; @@ -1006,18 +998,17 @@ thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset) /* Add all timers that have popped to the ready list. */ static unsigned int -thread_timer_process (struct thread_list *list, struct timeval *timenow) +thread_timer_process (struct pqueue *queue, struct timeval *timenow) { struct thread *thread; - struct thread *next; unsigned int ready = 0; - for (thread = list->head; thread; thread = next) + while (queue->size) { - next = thread->next; + thread = queue->array[0]; if (timeval_cmp (*timenow, thread->u.sands) < 0) return ready; - thread_list_delete (list, thread); + pqueue_dequeue(queue); thread->type = THREAD_READY; thread_list_add (&thread->master->ready, thread); ready++; @@ -1093,8 +1084,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (m->ready.count == 0) { quagga_get_relative (NULL); - timer_wait = thread_timer_wait (&m->timer, &timer_val); - timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); + timer_wait = thread_timer_wait (m->timer, &timer_val); + timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg); if (timer_wait_bg && (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) @@ -1150,7 +1141,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) priority than I/O threads, so let's push them onto the ready list in front of the I/O threads. */ quagga_get_relative (NULL); - thread_timer_process (&m->timer, &relative_time); + thread_timer_process (m->timer, &relative_time); /* Got IO, process it */ if (num > 0) @@ -1171,7 +1162,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) #endif /* Background timer/events, lowest priority */ - thread_timer_process (&m->background, &relative_time); + thread_timer_process (m->background, &relative_time); if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); @@ -1226,6 +1217,8 @@ thread_getrusage (RUSAGE_T *r) #endif /* HAVE_CLOCK_MONOTONIC */ } +struct thread *thread_current = NULL; + /* We check thread consumed time. If the system has getrusage, we'll use that to get in-depth stats on the performance of the thread in addition to wall clock time stats from gettimeofday. */ @@ -1246,7 +1239,7 @@ thread_call (struct thread *thread) struct cpu_thread_history tmp; tmp.func = thread->func; - strcpy(tmp.funcname, thread->funcname); + tmp.funcname = thread->funcname; thread->hist = hash_get (cpu_record, &tmp, (void * (*) (void *))cpu_record_hash_alloc); @@ -1255,7 +1248,9 @@ thread_call (struct thread *thread) GETRUSAGE (&before); thread->real = before.real; + thread_current = thread; (*thread->func) (thread); + thread_current = NULL; GETRUSAGE (&after); @@ -1294,7 +1289,7 @@ funcname_thread_execute (struct thread_master *m, int (*func)(struct thread *), void *arg, int val, - const char* funcname) + debugargdef) { struct thread dummy; @@ -1306,7 +1301,11 @@ funcname_thread_execute (struct thread_master *m, dummy.func = func; dummy.arg = arg; dummy.u.val = val; - strip_funcname (dummy.funcname, funcname); + + dummy.funcname = funcname; + dummy.schedfrom = schedfrom; + dummy.schedfrom_line = fromln; + thread_call (&dummy); return NULL; diff --git a/lib/thread.h b/lib/thread.h index 67902cf6..43ffbf60 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -1,6 +1,5 @@ /* Thread management routine header. * Copyright (C) 1998 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * @@ -44,16 +43,18 @@ struct thread_list int count; }; +struct pqueue; + /* Master of the theads. */ struct thread_master { struct thread_list read; struct thread_list write; - struct thread_list timer; + struct pqueue *timer; struct thread_list event; struct thread_list ready; struct thread_list unuse; - struct thread_list background; + struct pqueue *background; fd_set readfd; fd_set writefd; fd_set exceptfd; @@ -62,9 +63,6 @@ struct thread_master typedef unsigned char thread_type; -/* ISO C99 maximum function name length is 63 */ -#define FUNCNAME_LEN 64 - /* Thread itself. */ struct thread { @@ -80,9 +78,12 @@ struct thread int fd; /* file descriptor in case of read/write. */ struct timeval sands; /* rest of time sands value. */ } u; + int index; /* used for timers to store position in queue */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ - char funcname[FUNCNAME_LEN]; + const char *funcname; + const char *schedfrom; + int schedfrom_line; }; struct cpu_thread_history @@ -97,7 +98,7 @@ struct cpu_thread_history struct time_stats cpu; #endif thread_type types; - char funcname[FUNCNAME_LEN]; + const char *funcname; }; /* Clocks supported by Quagga */ @@ -162,15 +163,17 @@ enum quagga_clkid { #define THREAD_WRITE_OFF(thread) THREAD_OFF(thread) #define THREAD_TIMER_OFF(thread) THREAD_OFF(thread) -#define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f) -#define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f) -#define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f) -#define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f) -#define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f) -#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f) +#define debugargdef const char *funcname, const char *schedfrom, int fromln + +#define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__) /* The 4th arg to thread_add_background is the # of milliseconds to delay. */ -#define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f) +#define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f,__FILE__,__LINE__) /* Prototypes. */ extern struct thread_master *thread_master_create (void); @@ -178,33 +181,36 @@ extern void thread_master_free (struct thread_master *); extern struct thread *funcname_thread_add_read (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_write (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_timer (struct thread_master *, int (*)(struct thread *), - void *, long, const char*); + void *, long, debugargdef); extern struct thread *funcname_thread_add_timer_msec (struct thread_master *, int (*)(struct thread *), - void *, long, const char*); + void *, long, debugargdef); extern struct thread *funcname_thread_add_event (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_background (struct thread_master *, int (*func)(struct thread *), void *arg, long milliseconds_to_delay, - const char *funcname); + debugargdef); extern struct thread *funcname_thread_execute (struct thread_master *, int (*)(struct thread *), - void *, int, const char *); + void *, int, debugargdef); +#undef debugargdef + extern void thread_cancel (struct thread *); extern unsigned int thread_cancel_event (struct thread_master *, void *); extern struct thread *thread_fetch (struct thread_master *, struct thread *); extern void thread_call (struct thread *); extern unsigned long thread_timer_remain_second (struct thread *); extern int thread_should_yield (struct thread *); +extern unsigned long timeval_elapsed (struct timeval a, struct timeval b); /* Internal libzebra exports */ extern void thread_getrusage (RUSAGE_T *); @@ -228,4 +234,8 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, extern struct timeval recent_time; /* Similar to recent_time, but a monotonically increasing time value */ extern struct timeval recent_relative_time (void); + +/* only for use in logging functions! */ +extern struct thread *thread_current; + #endif /* _ZEBRA_THREAD_H */ diff --git a/lib/version.h.in b/lib/version.h.in index 429474d1..aef1d090 100644 --- a/lib/version.h.in +++ b/lib/version.h.in @@ -24,9 +24,20 @@ #ifndef _ZEBRA_VERSION_H #define _ZEBRA_VERSION_H +#ifdef GIT_VERSION +#include "gitversion.h" +#endif + +#ifndef GIT_SUFFIX +#define GIT_SUFFIX "" +#endif +#ifndef GIT_INFO +#define GIT_INFO "" +#endif + #define QUAGGA_PROGNAME "@PACKAGE_NAME@" -#define QUAGGA_VERSION "@PACKAGE_VERSION@" +#define QUAGGA_VERSION "@PACKAGE_VERSION@" GIT_SUFFIX #define ZEBRA_BUG_ADDRESS "@PACKAGE_BUGREPORT@" @@ -34,6 +45,8 @@ #define QUAGGA_COPYRIGHT "Copyright 1996-2005 Kunihiro Ishiguro, et al." +#define QUAGGA_CONFIG_ARGS "@CONFIG_ARGS@" + pid_t pid_output (const char *); #ifndef HAVE_DAEMON diff --git a/lib/vty.c b/lib/vty.c index 70bf5645..750f8856 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -57,7 +57,7 @@ static void vty_event (enum event, int, struct vty *); /* Extern host structure from command.c */ extern struct host host; - + /* Vector which store each vty structure. */ static vector vtyvec; @@ -71,7 +71,7 @@ static char *vty_accesslist_name = NULL; static char *vty_ipv6_accesslist_name = NULL; /* VTY server thread. */ -vector Vvty_serv_thread; +static vector Vvty_serv_thread; /* Current directory. */ char *vty_cwd = NULL; @@ -89,7 +89,7 @@ static u_char restricted_mode = 0; /* Integrated configuration file path */ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; - + /* VTY standard output function. */ int vty_out (struct vty *vty, const char *format, ...) @@ -455,7 +455,7 @@ vty_command (struct vty *vty, char *buf) return ret; } - + static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; @@ -713,6 +713,7 @@ vty_end_config (struct vty *vty) case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -931,23 +932,23 @@ vty_complete_command (struct vty *vty) static void vty_describe_fold (struct vty *vty, int cmd_width, - unsigned int desc_width, struct desc *desc) + unsigned int desc_width, struct cmd_token *token) { char *buf; const char *cmd, *p; int pos; - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; if (desc_width <= 0) { - vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); return; } - buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); + buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1); - for (p = desc->str; strlen (p) > desc_width; p += pos + 1) + for (p = token->desc; strlen (p) > desc_width; p += pos + 1) { for (pos = desc_width; pos > 0; pos--) if (*(p + pos) == ' ') @@ -976,7 +977,7 @@ vty_describe_command (struct vty *vty) vector vline; vector describe; unsigned int i, width, desc_width; - struct desc *desc, *desc_cr = NULL; + struct cmd_token *token, *token_cr = NULL; vline = cmd_make_strvec (vty->buf); @@ -1010,15 +1011,15 @@ vty_describe_command (struct vty *vty) /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { unsigned int len; - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') + len = strlen (token->cmd); + if (token->cmd[0] == '.') len--; if (width < len) @@ -1030,27 +1031,27 @@ vty_describe_command (struct vty *vty) /* Print out description. */ for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - if (strcmp (desc->cmd, command_cr) == 0) + if (strcmp (token->cmd, command_cr) == 0) { - desc_cr = desc; + token_cr = token; continue; } - if (!desc->str) + if (!token->desc) vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) + else if (desc_width >= strlen (token->desc)) vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, desc); + vty_describe_fold (vty, width, desc_width, token); #if 0 vty_out (vty, " %-*s %s%s", width @@ -1059,18 +1060,18 @@ vty_describe_command (struct vty *vty) #endif /* 0 */ } - if ((desc = desc_cr)) + if ((token = token_cr)) { - if (!desc->str) + if (!token->desc) vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) + else if (desc_width >= strlen (token->desc)) vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, desc); + vty_describe_fold (vty, width, desc_width, token); } out: @@ -1117,6 +1118,7 @@ vty_stop_input (struct vty *vty) case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -1773,7 +1775,7 @@ vty_accept (struct thread *thread) return 0; } -#if defined(HAVE_IPV6) && !defined(NRL) +#ifdef HAVE_IPV6 static void vty_serv_sock_addrinfo (const char *hostname, unsigned short port) { @@ -1838,7 +1840,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) freeaddrinfo (ainfo_save); } -#else /* HAVE_IPV6 && ! NRL */ +#else /* HAVE_IPV6 */ /* Make vty server socket. */ static void @@ -1856,9 +1858,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) { case AF_INET: naddr=&su.sin.sin_addr; + break; #ifdef HAVE_IPV6 case AF_INET6: naddr=&su.sin6.sin6_addr; + break; #endif } @@ -1904,7 +1908,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) /* Add vty server event. */ vty_event (VTY_SERV, accept_sock, NULL); } -#endif /* HAVE_IPV6 && ! NRL */ +#endif /* HAVE_IPV6 */ #ifdef VTYSH /* For sockaddr_un. */ @@ -2139,12 +2143,7 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path) { #ifdef HAVE_IPV6 -#ifdef NRL - vty_serv_sock_family (addr, port, AF_INET); - vty_serv_sock_family (addr, port, AF_INET6); -#else /* ! NRL */ vty_serv_sock_addrinfo (addr, port); -#endif /* NRL*/ #else /* ! HAVE_IPV6 */ vty_serv_sock_family (addr,port, AF_INET); #endif /* HAVE_IPV6 */ @@ -2227,28 +2226,37 @@ vty_read_file (FILE *confp) { int ret; struct vty *vty; + unsigned int line_num = 0; vty = vty_new (); - vty->fd = 0; /* stdout */ - vty->type = VTY_TERM; + vty->fd = dup(STDERR_FILENO); /* vty_close() will close this */ + if (vty->fd < 0) + { + /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */ + vty->fd = STDOUT_FILENO; + } + vty->type = VTY_FILE; vty->node = CONFIG_NODE; /* Execute configuration file */ - ret = config_from_file (vty, confp); + ret = config_from_file (vty, confp, &line_num); + + /* Flush any previous errors before printing messages below */ + buffer_flush_all (vty->obuf, vty->fd); if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { switch (ret) { case CMD_ERR_AMBIGUOUS: - fprintf (stderr, "Ambiguous command.\n"); + fprintf (stderr, "*** Error reading config: Ambiguous command.\n"); break; case CMD_ERR_NO_MATCH: - fprintf (stderr, "There is no such command.\n"); + fprintf (stderr, "*** Error reading config: There is no such command.\n"); break; } - fprintf (stderr, "Error occured during reading below line.\n%s\n", - vty->buf); + fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", + line_num, vty->buf); vty_close (vty); exit (1); } @@ -2447,7 +2455,7 @@ vty_log (const char *level, const char *proto_str, /* Async-signal-safe version of vty_log for fixed strings. */ void -vty_log_fixed (const char *buf, size_t len) +vty_log_fixed (char *buf, size_t len) { unsigned int i; struct iovec iov[2]; @@ -2456,7 +2464,7 @@ vty_log_fixed (const char *buf, size_t len) if (!vtyvec) return; - iov[0].iov_base = (void *)buf; + iov[0].iov_base = buf; iov[0].iov_len = len; iov[1].iov_base = (void *)"\r\n"; iov[1].iov_len = 2; @@ -2492,7 +2500,7 @@ vty_config_unlock (struct vty *vty) } return vty->config; } - + /* Master of the threads. */ static struct thread_master *master; @@ -2509,7 +2517,8 @@ vty_event (enum event event, int sock, struct vty *vty) break; #ifdef VTYSH case VTYSH_SERV: - thread_add_read (master, vtysh_accept, vty, sock); + vty_serv_thread = thread_add_read (master, vtysh_accept, vty, sock); + vector_set_index (Vvty_serv_thread, sock, vty_serv_thread); break; case VTYSH_READ: vty->t_read = thread_add_read (master, vtysh_read, vty, sock); @@ -2548,7 +2557,7 @@ vty_event (enum event event, int sock, struct vty *vty) break; } } - + DEFUN (config_who, config_who_cmd, "who", diff --git a/lib/vty.h b/lib/vty.h index e5158687..f31f4b5d 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -149,8 +149,8 @@ struct vty #define PRINTF_ATTRIBUTE(a,b) #endif /* __GNUC__ */ -/* Utility macros to convert VTY argument to unsigned long or integer. */ -#define VTY_GET_LONG(NAME,V,STR) \ +/* Utility macros to convert VTY argument to unsigned long */ +#define VTY_GET_ULONG(NAME,V,STR) \ do { \ char *endptr = NULL; \ errno = 0; \ @@ -162,20 +162,38 @@ do { \ } \ } while (0) -#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX) \ -do { \ - unsigned long tmpl; \ - VTY_GET_LONG(NAME, tmpl, STR); \ - if ( (tmpl < (MIN)) || (tmpl > (MAX))) \ - { \ - vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - (V) = tmpl; \ +/* + * The logic below ((TMPL) <= ((MIN) && (TMPL) != (MIN)) is + * done to circumvent the compiler complaining about + * comparing unsigned numbers against zero, if MIN is zero. + * NB: The compiler isn't smart enough to supress the warning + * if you write (MIN) != 0 && tmpl < (MIN). + */ +#define VTY_GET_INTEGER_RANGE_HEART(NAME,TMPL,STR,MIN,MAX) \ +do { \ + VTY_GET_ULONG(NAME, (TMPL), STR); \ + if ( ((TMPL) <= (MIN) && (TMPL) != (MIN)) || (TMPL) > (MAX) ) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE);\ + return CMD_WARNING; \ + } \ } while (0) -#define VTY_GET_INTEGER(NAME,V,STR) \ - VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX) +#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX) \ +do { \ + unsigned long tmpl; \ + VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX); \ + (V) = tmpl; \ +} while (0) + +#define VTY_CHECK_INTEGER_RANGE(NAME,STR,MIN,MAX) \ +do { \ + unsigned long tmpl; \ + VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX); \ +} while (0) + +#define VTY_GET_INTEGER(NAME,V,STR) \ + VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX) #define VTY_GET_IPV4_ADDRESS(NAME,V,STR) \ do { \ @@ -199,6 +217,14 @@ do { } \ } while (0) +#define VTY_WARN_EXPERIMENTAL() \ +do { \ + vty_out (vty, "%% WARNING: this command is experimental. Both its name and" \ + " parameters may%s%% change in a future version of Quagga," \ + " possibly breaking your configuration!%s", \ + VTY_NEWLINE, VTY_NEWLINE); \ +} while (0) + /* Exported variables */ extern char integrate_default[]; @@ -224,6 +250,6 @@ extern void vty_hello (struct vty *); /* Send a fixed-size message to all vty terminal monitors; this should be an async-signal-safe function. */ -extern void vty_log_fixed (const char *buf, size_t len); +extern void vty_log_fixed (char *buf, size_t len); #endif /* _ZEBRA_VTY_H */ diff --git a/lib/workqueue.c b/lib/workqueue.c index 61643bf8..e09d009f 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -30,7 +30,11 @@ #include "log.h" /* master list of work_queues */ -static struct list work_queues; +static struct list _work_queues; +/* pointer primarily to avoid an otherwise harmless warning on + * ALL_LIST_ELEMENTS_RO + */ +static struct list *work_queues = &_work_queues; #define WORK_QUEUE_MIN_GRANULARITY 1 @@ -78,7 +82,7 @@ work_queue_new (struct thread_master *m, const char *queue_name) new->items->del = (void (*)(void *)) work_queue_item_free; - listnode_add (&work_queues, new); + listnode_add (work_queues, new); new->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY; @@ -96,7 +100,7 @@ work_queue_free (struct work_queue *wq) /* list_delete frees items via callback */ list_delete (wq->items); - listnode_delete (&work_queues, wq); + listnode_delete (work_queues, wq); XFREE (MTYPE_WORK_QUEUE_NAME, wq->name); XFREE (MTYPE_WORK_QUEUE, wq); @@ -187,7 +191,7 @@ DEFUN(show_work_queues, "Name", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO ((&work_queues), node, wq)) + for (ALL_LIST_ELEMENTS_RO (work_queues, node, wq)) { vty_out (vty,"%c %8d %5d %8ld %7d %6d %6u %s%s", (CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'), diff --git a/lib/zassert.h b/lib/zassert.h index 79126760..bf0a851b 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -1,5 +1,22 @@ /* * $Id: zassert.h,v 1.2 2004/12/03 18:01:04 ajs Exp $ + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. */ #ifndef _QUAGGA_ASSERT_H diff --git a/lib/zclient.c b/lib/zclient.c index 61c6f730..41ecbb61 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -32,7 +32,7 @@ #include "zclient.h" #include "memory.h" #include "table.h" - + /* Zebra client events. */ enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT}; @@ -45,7 +45,7 @@ char *zclient_serv_path = NULL; /* This file local debug flag. */ int zclient_debug = 0; - + /* Allocate zclient structure. */ struct zclient * zclient_new () @@ -229,7 +229,7 @@ zclient_socket_connect (struct zclient *zclient) #ifdef HAVE_TCP_ZEBRA zclient->sock = zclient_socket (); #else - zclient->sock = zclient_socket_un (zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH); + zclient->sock = zclient_socket_un (zclient_serv_path_get()); #endif return zclient->sock; } @@ -413,7 +413,7 @@ zclient_connect (struct thread *t) return zclient_start (zclient); } - + /* * "xdr_encode"-like interface that allows daemon (client) to send * a message to zebra server for a route that needs to be @@ -734,7 +734,7 @@ zebra_interface_if_set_value (struct stream *s, struct interface *ifp) ifp->mtu6 = stream_getl (s); ifp->bandwidth = stream_getl (s); #ifdef HAVE_STRUCT_SOCKADDR_DL - stream_get (&ifp->sdl, s, sizeof (ifp->sdl)); + stream_get (&ifp->sdl, s, sizeof (ifp->sdl_storage)); #else ifp->hw_addr_len = stream_getl (s); if (ifp->hw_addr_len) @@ -805,6 +805,16 @@ zebra_interface_address_read (int type, struct stream *s) ifc->flags = ifc_flags; if (ifc->destination) ifc->destination->prefixlen = ifc->address->prefixlen; + else if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) + { + /* carp interfaces on OpenBSD with 0.0.0.0/0 as "peer" */ + char buf[BUFSIZ]; + prefix2str (ifc->address, buf, sizeof(buf)); + zlog_warn("warning: interface %s address %s " + "with peer flag set, but no peer address!", + ifp->name, buf); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } } } else @@ -816,7 +826,7 @@ zebra_interface_address_read (int type, struct stream *s) return ifc; } - + /* Zebra client message read function. */ static int zclient_read (struct thread *thread) @@ -1043,6 +1053,11 @@ zclient_event (enum event event, struct zclient *zclient) } } +const char *const zclient_serv_path_get() +{ + return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH; +} + void zclient_serv_path_set (char *path) { diff --git a/lib/zclient.h b/lib/zclient.h index a660bbf1..d0c5450b 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -134,6 +134,7 @@ extern void zclient_free (struct zclient *); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); +extern const char *const zclient_serv_path_get (void); /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send (int command, struct zclient *, int type); diff --git a/lib/zebra.h b/lib/zebra.h index f8a6be30..a5ed20e4 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -40,6 +40,7 @@ typedef int socklen_t; #include #include #include +#include #include #include #include @@ -50,7 +51,6 @@ typedef int socklen_t; #ifdef HAVE_STROPTS_H #include #endif /* HAVE_STROPTS_H */ -#include #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ @@ -142,6 +142,10 @@ typedef int socklen_t; #include #endif /* HAVE_SYS_SOCKIO_H */ +#ifdef __APPLE__ +#define __APPLE_USE_RFC_3542 +#endif + #ifdef HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ @@ -171,7 +175,6 @@ typedef int socklen_t; #include #include #include -#include #else #define RT_TABLE_MAIN 0 #endif /* HAVE_NETLINK */ @@ -241,20 +244,6 @@ typedef int socklen_t; #include #endif /* HAVE_GLIBC_BACKTRACE */ -#ifdef BSDI_NRL - -#ifdef HAVE_NETINET6_IN6_H -#include -#endif /* HAVE_NETINET6_IN6_H */ - -#ifdef NRL -#include -#endif /* NRL */ - -#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL - -#endif /* BSDI_NRL */ - /* Local includes: */ #if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL)) #define __attribute__(x) @@ -355,7 +344,8 @@ struct in_pktinfo * OpenBSD: network byte order, apart from older versions which are as per * *BSD */ -#if defined(__NetBSD__) || defined(__FreeBSD__) \ +#if defined(__NetBSD__) \ + || (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) \ || (defined(__OpenBSD__) && (OpenBSD < 200311)) \ || (defined(__APPLE__)) \ || (defined(SUNOS_5) && defined(WORDS_BIGENDIAN)) @@ -387,6 +377,8 @@ struct in_pktinfo #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif +#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) + /* For old definition. */ #ifndef IN6_ARE_ADDR_EQUAL #define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL @@ -419,7 +411,8 @@ struct in_pktinfo #define ZEBRA_ROUTER_ID_DELETE 21 #define ZEBRA_ROUTER_ID_UPDATE 22 #define ZEBRA_HELLO 23 -#define ZEBRA_MESSAGE_MAX 24 +#define ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB 24 +#define ZEBRA_MESSAGE_MAX 25 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4 new file mode 100644 index 00000000..27b0f0c4 --- /dev/null +++ b/m4/ax_sys_weak_alias.m4 @@ -0,0 +1,333 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SYS_WEAK_ALIAS +# +# DESCRIPTION +# +# Determines whether weak aliases are supported on the system, and if so, +# what scheme is used to declare them. Also checks to see if aliases can +# cross object file boundaries, as some systems don't permit them to. +# +# Most systems permit something called a "weak alias" or "weak symbol." +# These aliases permit a library to provide a stub form of a routine +# defined in another library, thus allowing the first library to operate +# even if the other library is not linked. This macro will check for +# support of weak aliases, figure out what schemes are available, and +# determine some characteristics of the weak alias support -- primarily, +# whether a weak alias declared in one object file may be referenced from +# another object file. +# +# There are four known schemes of declaring weak symbols; each scheme is +# checked in turn, and the first one found is prefered. Note that only one +# of the mentioned preprocessor macros will be defined! +# +# 1. Function attributes +# +# This scheme was first introduced by the GNU C compiler, and attaches +# attributes to particular functions. It is among the easiest to use, and +# so is the first one checked. If this scheme is detected, the +# preprocessor macro HAVE_SYS_WEAK_ALIAS_ATTRIBUTE will be defined to 1. +# This scheme is used as in the following code fragment: +# +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# void weakf(int c) __attribute__((weak, alias("__weakf"))); +# +# 2. #pragma weak +# +# This scheme is in use by many compilers other than the GNU C compiler. +# It is also particularly easy to use, and fairly portable -- well, as +# portable as these things get. If this scheme is detected first, the +# preprocessor macro HAVE_SYS_WEAK_ALIAS_PRAGMA will be defined to 1. This +# scheme is used as in the following code fragment: +# +# extern void weakf(int c); +# #pragma weak weakf = __weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# 3. #pragma _HP_SECONDARY_DEF +# +# This scheme appears to be in use by the HP compiler. As it is rather +# specialized, this is one of the last schemes checked. If it is the first +# one detected, the preprocessor macro HAVE_SYS_WEAK_ALIAS_HPSECONDARY +# will be defined to 1. This scheme is used as in the following code +# fragment: +# +# extern void weakf(int c); +# #pragma _HP_SECONDARY_DEF __weakf weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# 4. #pragma _CRI duplicate +# +# This scheme appears to be in use by the Cray compiler. As it is rather +# specialized, it too is one of the last schemes checked. If it is the +# first one detected, the preprocessor macro +# HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE will be defined to 1. This scheme is +# used as in the following code fragment: +# +# extern void weakf(int c); +# #pragma _CRI duplicate weakf as __weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# In addition to the preprocessor macros listed above, if any scheme is +# found, the preprocessor macro HAVE_SYS_WEAK_ALIAS will also be defined +# to 1. +# +# Once a weak aliasing scheme has been found, a check will be performed to +# see if weak aliases are honored across object file boundaries. If they +# are, the HAVE_SYS_WEAK_ALIAS_CROSSFILE preprocessor macro is defined to +# 1. +# +# This Autoconf macro also makes two substitutions. The first, WEAK_ALIAS, +# contains the name of the scheme found (one of "attribute", "pragma", +# "hpsecondary", or "criduplicate"), or "no" if no weak aliasing scheme +# was found. The second, WEAK_ALIAS_CROSSFILE, is set to "yes" or "no" +# depending on whether or not weak aliases may cross object file +# boundaries. +# +# LICENSE +# +# Copyright (c) 2008 Kevin L. Mitchell +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AU_ALIAS([KLM_SYS_WEAK_ALIAS], [AX_SYS_WEAK_ALIAS]) +AC_DEFUN([AX_SYS_WEAK_ALIAS], [ + # starting point: no aliasing scheme yet... + ax_sys_weak_alias=no + + # Figure out what kind of aliasing may be supported... + _AX_SYS_WEAK_ALIAS_ATTRIBUTE + _AX_SYS_WEAK_ALIAS_PRAGMA + _AX_SYS_WEAK_ALIAS_HPSECONDARY + _AX_SYS_WEAK_ALIAS_CRIDUPLICATE + + # Do we actually support aliasing? + AC_CACHE_CHECK([how to create weak aliases with $CC], + [ax_cv_sys_weak_alias], + [ax_cv_sys_weak_alias=$ax_sys_weak_alias]) + + # OK, set a #define + AS_IF([test $ax_cv_sys_weak_alias != no], [ + AC_DEFINE([HAVE_SYS_WEAK_ALIAS], 1, + [Define this if your system can create weak aliases]) + ]) + + # Can aliases cross object file boundaries? + _AX_SYS_WEAK_ALIAS_CROSSFILE + + # OK, remember the results + AC_SUBST([WEAK_ALIAS], [$ax_cv_sys_weak_alias]) + AC_SUBST([WEAK_ALIAS_CROSSFILE], [$ax_cv_sys_weak_alias_crossfile]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_ATTRIBUTE], +[ # Test whether compiler accepts __attribute__ form of weak aliasing + AC_CACHE_CHECK([whether $CC accepts function __attribute__((weak,alias()))], + [ax_cv_sys_weak_alias_attribute], [ + # We add -Werror if it's gcc to force an error exit if the weak attribute + # isn't understood + AS_IF([test $GCC = yes], [ + save_CFLAGS=$CFLAGS + CFLAGS=-Werror]) + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +void __weakf(int c) {} +void weakf(int c) __attribute__((weak, alias("__weakf")));], + [weakf(0)])], + [ax_cv_sys_weak_alias_attribute=yes], + [ax_cv_sys_weak_alias_attribute=no]) + + # Restore original CFLAGS + AS_IF([test $GCC = yes], [ + CFLAGS=$save_CFLAGS]) + ]) + + # What was the result of the test? + AS_IF([test $ax_cv_sys_weak_alias_attribute = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=attribute + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1, + [Define this if weak aliases may be created with __attribute__]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_PRAGMA], +[ # Test whether compiler accepts #pragma form of weak aliasing + AC_CACHE_CHECK([whether $CC supports @%:@pragma weak], + [ax_cv_sys_weak_alias_pragma], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma weak weakf = __weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_pragma=yes], + [ax_cv_sys_weak_alias_pragma=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_cv_sys_weak_alias_pragma = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=pragma + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1, + [Define this if weak aliases may be created with @%:@pragma weak]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_HPSECONDARY], +[ # Test whether compiler accepts _HP_SECONDARY_DEF pragma from HP... + AC_CACHE_CHECK([whether $CC supports @%:@pragma _HP_SECONDARY_DEF], + [ax_cv_sys_weak_alias_hpsecondary], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma _HP_SECONDARY_DEF __weakf weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_hpsecondary=yes], + [ax_cv_sys_weak_alias_hpsecondary=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_cv_sys_weak_alias_hpsecondary = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=hpsecondary + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1, + [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_CRIDUPLICATE], +[ # Test whether compiler accepts "_CRI duplicate" pragma from Cray + AC_CACHE_CHECK([whether $CC supports @%:@pragma _CRI duplicate], + [ax_cv_sys_weak_alias_criduplicate], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma _CRI duplicate weakf as __weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_criduplicate=yes], + [ax_cv_sys_weak_alias_criduplicate=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_cv_sys_weak_alias_criduplicate = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=criduplicate + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1, + [Define this if weak aliases may be created with @%:@pragma _CRI duplicate]) + ]) +]) + +dnl Note: This macro is modeled closely on AC_LINK_IFELSE, and in fact +dnl depends on some implementation details of that macro, particularly +dnl its use of _AC_MSG_LOG_CONFTEST to log the failed test program and +dnl its use of ac_link for running the linker. +AC_DEFUN([_AX_SYS_WEAK_ALIAS_CROSSFILE], +[ # Check to see if weak aliases can cross object file boundaries + AC_CACHE_CHECK([whether $CC supports weak aliases across object file boundaries], + [ax_cv_sys_weak_alias_crossfile], [ + AS_IF([test $ax_cv_sys_weak_alias = no], + [ax_cv_sys_weak_alias_crossfile=no], [ +dnl Must build our own test files... + # conftest1 contains our weak alias definition... + cat >conftest1.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF + cat confdefs.h >>conftest1.$ac_ext + cat >>conftest1.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +@%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +extern void weakf(int c); +@%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) +@%:@pragma weak weakf = __weakf +@%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY) +@%:@pragma _HP_SECONDARY_DEF __weakf weakf +@%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE) +@%:@pragma _CRI duplicate weakf as __weakf +@%:@endif +@%:@endif +void __weakf(int c) {} +@%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +void weakf(int c) __attribute((weak, alias("__weakf"))); +@%:@endif +_ACEOF + # And conftest2 contains our main routine that calls it + cat >conftest2.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF + cat confdefs.h >> conftest2.$ac_ext + cat >>conftest2.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +extern void weakf(int c); +int +main () +{ + weakf(0); + return 0; +} +_ACEOF + # We must remove the object files (if any) ourselves... + rm -f conftest2.$ac_objext conftest$ac_exeext + + # Change ac_link to compile *2* files together + save_aclink=$ac_link + ac_link=`echo "$ac_link" | \ + sed -e 's/conftest\(\.\$ac_ext\)/conftest1\1 conftest2\1/'` +dnl Substitute our own routine for logging the conftest +m4_pushdef([_AC_MSG_LOG_CONFTEST], +[echo "$as_me: failed program was:" >&AS_MESSAGE_LOG_FD +echo ">>> conftest1.$ac_ext" >&AS_MESSAGE_LOG_FD +sed "s/^/| /" conftest1.$ac_ext >&AS_MESSAGE_LOG_FD +echo ">>> conftest2.$ac_ext" >&AS_MESSAGE_LOG_FD +sed "s/^/| /" conftest2.$ac_ext >&AS_MESSAGE_LOG_FD +])dnl + # Since we created the files ourselves, don't use SOURCE argument + AC_LINK_IFELSE(, [ax_cv_sys_weak_alias_crossfile=yes], + [ax_cv_sys_weak_alias_crossfile=no]) +dnl Restore _AC_MSG_LOG_CONFTEST +m4_popdef([_AC_MSG_LOG_CONFTEST])dnl + # Restore ac_link + ac_link=$save_aclink + + # We must remove the object files (if any) and C files ourselves... + rm -f conftest1.$ac_ext conftest2.$ac_ext \ + conftest1.$ac_objext conftest2.$ac_objext + ]) + ]) + + # What were the results of the test? + AS_IF([test $ax_cv_sys_weak_alias_crossfile = yes], [ + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CROSSFILE], 1, + [Define this if weak aliases in other files are honored]) + ]) +]) diff --git a/ospf6d/Makefile.am b/ospf6d/Makefile.am index 726ce543..d30ce581 100644 --- a/ospf6d/Makefile.am +++ b/ospf6d/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ospf6d/README b/ospf6d/README index 883486fa..f5a00464 100644 --- a/ospf6d/README +++ b/ospf6d/README @@ -60,7 +60,7 @@ CONFIG NODE: INTERFACE NODE: ipv6 ospf6 cost COST - Sets the interface's output cost. default 1 + Sets the interface's output cost. Depends on interface bandwidth by default. ipv6 ospf6 hello-interval HELLOINTERVAL Sets the interface's Hello Interval. default 10 @@ -85,6 +85,15 @@ OSPF6 NODE: Binds interface to specified Area, and start sending OSPFv3 packets. + auto-cost reference-bandwidth COST + Sets the reference bandwidth for cost calculations, where this + bandwidth is considered equivalent to an OSPF cost of 1, specified + in Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth + 100Mbit/s or higher will have a cost of 1. Cost of lower bandwidth + links will be scaled with reference to this cost). This + configuration setting MUST be consistent across all routers within + the OSPF domain. + Sample configuration is in ospf6d.conf.sample. -- diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index c3a63fe6..bb79900e 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -253,7 +253,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route, } /* do not generate if the route cost is greater or equal to LSInfinity */ - if (route->path.cost >= LS_INFINITY) + if (route->path.cost >= OSPF_LS_INFINITY) { if (is_debug) zlog_debug ("The cost exceeds LSInfinity, withdraw"); @@ -296,7 +296,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route, /* ranges are ignored when originate backbone routes to transit area. Otherwise, if ranges are configured, the route is suppressed. */ if (range && ! CHECK_FLAG (range->flag, OSPF6_ROUTE_REMOVE) && - (route->path.area_id != BACKBONE_AREA_ID || + (route->path.area_id != OSPF_AREA_BACKBONE || ! IS_AREA_TRANSIT (area))) { if (is_debug) @@ -537,13 +537,13 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) int i; char buf[64]; int is_debug = 0; + struct ospf6_inter_prefix_lsa *prefix_lsa = NULL; + struct ospf6_inter_router_lsa *router_lsa = NULL; memset (&prefix, 0, sizeof (prefix)); if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX)) { - struct ospf6_inter_prefix_lsa *prefix_lsa; - if (IS_OSPF6_DEBUG_EXAMIN (INTER_PREFIX)) { is_debug++; @@ -564,8 +564,6 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } else if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER)) { - struct ospf6_inter_router_lsa *router_lsa; - if (IS_OSPF6_DEBUG_EXAMIN (INTER_ROUTER)) { is_debug++; @@ -604,7 +602,7 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } /* (1) if cost == LSInfinity or if the LSA is MaxAge */ - if (cost == LS_INFINITY) + if (cost == OSPF_LS_INFINITY) { if (is_debug) zlog_debug ("cost is LS_INFINITY, ignore"); @@ -632,6 +630,7 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } /* (3) if the prefix is equal to an active configured address range */ + /* or if the NU bit is set in the prefix */ if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX)) { range = ospf6_route_lookup (&prefix, oa->range_table); @@ -643,6 +642,32 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) ospf6_route_remove (old, table); return; } + + if (CHECK_FLAG (prefix_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_NU) || + CHECK_FLAG (prefix_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_LA)) + { + if (is_debug) + zlog_debug ("Prefix has NU/LA bit set, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + } + + if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER)) + { + /* To pass test suites */ + if (! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_R) || + ! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_V6)) + { + if (is_debug) + zlog_debug ("Prefix has NU/LA bit set, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } } /* (4) if the routing table entry for the ABR does not exist */ @@ -762,14 +787,36 @@ ospf6_abr_reimport (struct ospf6_area *oa) } - + /* Display functions */ +static char * +ospf6_inter_area_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct in6_addr in6; + + if (lsa != NULL) + { + prefix_lsa = (struct ospf6_inter_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + ospf6_prefix_in6_addr (&in6, &prefix_lsa->prefix); + if (buf) + { + inet_ntop (AF_INET6, &in6, buf, buflen); + sprintf (&buf[strlen(buf)], "/%d", prefix_lsa->prefix.prefix_length); + } + } + + return (buf); +} + static int ospf6_inter_area_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { struct ospf6_inter_prefix_lsa *prefix_lsa; - struct in6_addr in6; - char buf[64]; + char buf[INET6_ADDRSTRLEN]; prefix_lsa = (struct ospf6_inter_prefix_lsa *) OSPF6_LSA_HEADER_END (lsa->header); @@ -781,14 +828,32 @@ ospf6_inter_area_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) buf, sizeof (buf)); vty_out (vty, " Prefix Options: %s%s", buf, VNL); - ospf6_prefix_in6_addr (&in6, &prefix_lsa->prefix); - inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); - vty_out (vty, " Prefix: %s/%d%s", buf, - prefix_lsa->prefix.prefix_length, VNL); + vty_out (vty, " Prefix: %s%s", + ospf6_inter_area_prefix_lsa_get_prefix_str (lsa, buf, sizeof(buf), + 0), VNL); return 0; } +static char * +ospf6_inter_area_router_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_inter_router_lsa *router_lsa; + + if (lsa != NULL) + { + router_lsa = (struct ospf6_inter_router_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + + if (buf) + inet_ntop (AF_INET, &router_lsa->router_id, buf, buflen); + } + + return (buf); +} + static int ospf6_inter_area_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -802,6 +867,7 @@ ospf6_inter_area_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, " Options: %s%s", buf, VNL); vty_out (vty, " Metric: %lu%s", (u_long) OSPF6_ABR_SUMMARY_METRIC (router_lsa), VNL); + inet_ntop (AF_INET, &router_lsa->router_id, buf, sizeof (buf)); vty_out (vty, " Destination Router ID: %s%s", buf, VNL); @@ -855,14 +921,18 @@ struct ospf6_lsa_handler inter_prefix_handler = { OSPF6_LSTYPE_INTER_PREFIX, "Inter-Prefix", - ospf6_inter_area_prefix_lsa_show + "IAP", + ospf6_inter_area_prefix_lsa_show, + ospf6_inter_area_prefix_lsa_get_prefix_str, }; struct ospf6_lsa_handler inter_router_handler = { OSPF6_LSTYPE_INTER_ROUTER, "Inter-Router", - ospf6_inter_area_router_lsa_show + "IAR", + ospf6_inter_area_router_lsa_show, + ospf6_inter_area_router_lsa_get_prefix_str, }; void diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index 816f5964..e5e2660b 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -24,6 +24,8 @@ /* for struct ospf6_route */ #include "ospf6_route.h" +/* for struct ospf6_prefix */ +#include "ospf6_proto.h" /* Debug option */ extern unsigned char conf_debug_ospf6_abr; diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 9934e6b9..9b704221 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -67,7 +67,8 @@ ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsadd_to_spf_reason(lsa)); break; case OSPF6_LSTYPE_INTRA_PREFIX: @@ -97,7 +98,8 @@ ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsremove_to_spf_reason(lsa)); break; case OSPF6_LSTYPE_INTRA_PREFIX: @@ -164,9 +166,18 @@ ospf6_area_create (u_int32_t area_id, struct ospf6 *o) oa->summary_router->scope = oa; /* set default options */ - OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER)) + { + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R); + } + else + { + OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); + } + OSPF6_OPT_SET (oa->options, OSPF6_OPT_E); - OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); oa->ospf6 = o; listnode_add_sort (o->area_list, oa); @@ -182,18 +193,21 @@ ospf6_area_create (u_int32_t area_id, struct ospf6 *o) void ospf6_area_delete (struct ospf6_area *oa) { - struct listnode *n, *nnode; + struct listnode *n; struct ospf6_interface *oi; ospf6_route_table_delete (oa->range_table); ospf6_route_table_delete (oa->summary_prefix); ospf6_route_table_delete (oa->summary_router); - /* ospf6 interface list */ - for (ALL_LIST_ELEMENTS (oa->if_list, n, nnode, oi)) - { - ospf6_interface_delete (oi); - } + /* The ospf6_interface structs store configuration + * information which should not be lost/reset when + * deleting an area. + * So just detach the interface from the area and + * keep it around. */ + for (ALL_LIST_ELEMENTS_RO (oa->if_list, n, oi)) + oi->area = NULL; + list_delete (oa->if_list); ospf6_lsdb_delete (oa->lsdb); @@ -246,6 +260,7 @@ ospf6_area_enable (struct ospf6_area *oa) for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) ospf6_interface_enable (oi); + ospf6_abr_enable_area (oa); } void @@ -258,9 +273,22 @@ ospf6_area_disable (struct ospf6_area *oa) for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) ospf6_interface_disable (oi); + + ospf6_abr_disable_area (oa); + ospf6_lsdb_remove_all (oa->lsdb); + ospf6_lsdb_remove_all (oa->lsdb_self); + + ospf6_spf_table_finish(oa->spf_table); + ospf6_route_remove_all(oa->route_table); + + THREAD_OFF (oa->thread_spf_calculation); + THREAD_OFF (oa->thread_route_calculation); + + THREAD_OFF (oa->thread_router_lsa); + THREAD_OFF (oa->thread_intra_prefix_lsa); } - + void ospf6_area_show (struct vty *vty, struct ospf6_area *oa) { @@ -401,6 +429,7 @@ DEFUN (no_area_range, } ospf6_route_remove (range, oa->range_table); + return CMD_SUCCESS; } @@ -489,13 +518,11 @@ DEFUN (no_area_filter_list, "Filter networks sent from this area\n") { struct ospf6_area *area; - struct prefix_list *plist; OSPF6_CMD_AREA_GET (argv[0], area); argc--; argv++; - plist = prefix_list_lookup (AFI_IP6, argv[0]); if (strncmp (argv[1], "in", 2) == 0) { if (PREFIX_NAME_IN (area)) diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index e8a4fbd8..655967a9 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -105,8 +105,6 @@ struct ospf6_area #define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */ #define OSPF6_AREA_STUB 0x08 -#define BACKBONE_AREA_ID (htonl (0)) -#define IS_AREA_BACKBONE(oa) ((oa)->area_id == BACKBONE_AREA_ID) #define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE)) #define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE)) #define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT)) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index ae0a286d..b1620d4a 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -58,18 +58,13 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) { char buffer[OSPF6_MAX_LSASIZE]; struct ospf6_lsa_header *lsa_header; - struct ospf6_lsa *old, *lsa; + struct ospf6_lsa *lsa; struct ospf6_external_info *info = route->route_option; struct ospf6_as_external_lsa *as_external_lsa; char buf[64]; caddr_t p; - /* find previous LSA */ - old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_AS_EXTERNAL), - route->path.origin.id, ospf6->router_id, - ospf6->lsdb); - if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE (AS_EXTERNAL)) { prefix2str (&route->prefix, buf, sizeof (buf)); @@ -151,7 +146,7 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) ospf6_lsa_originate_process (lsa, ospf6); } - + void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) { @@ -174,13 +169,20 @@ ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) return; } - if (OSPF6_ASBR_METRIC (external) == LS_INFINITY) + if (OSPF6_ASBR_METRIC (external) == OSPF_LS_INFINITY) { if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) zlog_debug ("Ignore LSA with LSInfinity Metric"); return; } + if (CHECK_FLAG(external->prefix.prefix_options, OSPF6_PREFIX_OPTION_NU)) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Ignore LSA with NU bit set Metric"); + return; + } + ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &asbr_id); asbr_entry = ospf6_route_lookup (&asbr_id, ospf6->brouter_table); if (asbr_entry == NULL || @@ -290,6 +292,8 @@ ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa) } ospf6_route_remove (route, ospf6->route_table); } + if (route != NULL) + ospf6_route_unlock (route); } void @@ -333,7 +337,7 @@ ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry) } - + /* redistribute function */ static void @@ -402,6 +406,8 @@ ospf6_asbr_redistribute_unset (int type) ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex, &route->prefix); } + + ospf6_asbr_routemap_unset (type); } void @@ -629,7 +635,6 @@ DEFUN (ospf6_redistribute, return CMD_WARNING; ospf6_asbr_redistribute_unset (type); - ospf6_asbr_routemap_unset (type); ospf6_asbr_redistribute_set (type); return CMD_SUCCESS; } @@ -670,7 +675,6 @@ DEFUN (no_ospf6_redistribute, return CMD_WARNING; ospf6_asbr_redistribute_unset (type); - ospf6_asbr_routemap_unset (type); return CMD_SUCCESS; } @@ -739,7 +743,7 @@ ospf6_redistribute_show_config (struct vty *vty) } - + /* Routemap Functions */ static route_map_result_t ospf6_routemap_rule_match_address_prefixlist (void *rule, @@ -890,7 +894,7 @@ ospf6_routemap_rule_set_metric_compile (const char *arg) u_int32_t metric; char *endp; metric = strtoul (arg, &endp, 0); - if (metric > LS_INFINITY || *endp != '\0') + if (metric > OSPF_LS_INFINITY || *endp != '\0') return NULL; return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); } @@ -1159,14 +1163,46 @@ ospf6_routemap_init (void) install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd); } - + /* Display functions */ +static char * +ospf6_as_external_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_as_external_lsa *external; + struct in6_addr in6; + int prefix_length = 0; + + if (lsa) + { + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (pos == 0) + { + ospf6_prefix_in6_addr (&in6, &external->prefix); + prefix_length = external->prefix.prefix_length; + } + else { + in6 = *((struct in6_addr *) + ((caddr_t) external + sizeof (struct ospf6_as_external_lsa) + + OSPF6_PREFIX_SPACE (external->prefix.prefix_length))); + } + if (buf) + { + inet_ntop (AF_INET6, &in6, buf, buflen); + if (prefix_length) + sprintf (&buf[strlen(buf)], "/%d", prefix_length); + } + } + return (buf); +} + static int ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { struct ospf6_as_external_lsa *external; char buf[64]; - struct in6_addr in6, *forwarding; assert (lsa->header); external = (struct ospf6_as_external_lsa *) @@ -1191,19 +1227,15 @@ ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) ntohs (external->prefix.prefix_refer_lstype), VNL); - ospf6_prefix_in6_addr (&in6, &external->prefix); - inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); - vty_out (vty, " Prefix: %s/%d%s", buf, - external->prefix.prefix_length, VNL); + vty_out (vty, " Prefix: %s%s", + ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 0), VNL); /* Forwarding-Address */ if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F)) { - forwarding = (struct in6_addr *) - ((caddr_t) external + sizeof (struct ospf6_as_external_lsa) + - OSPF6_PREFIX_SPACE (external->prefix.prefix_length)); - inet_ntop (AF_INET6, forwarding, buf, sizeof (buf)); - vty_out (vty, " Forwarding-Address: %s%s", buf, VNL); + vty_out (vty, " Forwarding-Address: %s%s", + ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 1), + VNL); } return 0; @@ -1257,7 +1289,9 @@ struct ospf6_lsa_handler as_external_handler = { OSPF6_LSTYPE_AS_EXTERNAL, "AS-External", - ospf6_as_external_lsa_show + "ASE", + ospf6_as_external_lsa_show, + ospf6_as_external_lsa_get_prefix_str }; void @@ -1275,6 +1309,20 @@ ospf6_asbr_init (void) install_element (OSPF6_NODE, &no_ospf6_redistribute_cmd); } +void +ospf6_asbr_redistribute_reset (void) +{ + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_OSPF6) + continue; + if (ospf6_zebra_is_redistribute (type)) + ospf6_asbr_redistribute_unset(type); + } +} + void ospf6_asbr_terminate (void) { diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 72e49143..73770cc0 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -89,6 +89,7 @@ extern void ospf6_asbr_redistribute_remove (int type, int ifindex, extern int ospf6_redistribute_config_write (struct vty *vty); extern void ospf6_asbr_init (void); +extern void ospf6_asbr_redistribute_reset (void); extern void ospf6_asbr_terminate (void); extern int config_write_ospf6_debug_asbr (struct vty *vty); diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index b8159729..49ed6e26 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -113,7 +113,7 @@ ospf6_lsa_originate (struct ospf6_lsa *lsa) ospf6_lsdb_add (ospf6_lsa_copy (lsa), lsdb_self); lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa, - LS_REFRESH_TIME); + OSPF_LS_REFRESH_TIME); if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type)) @@ -122,10 +122,8 @@ ospf6_lsa_originate (struct ospf6_lsa *lsa) ospf6_lsa_header_print (lsa); } - if (old) - ospf6_flood_clear (old); - ospf6_flood (NULL, lsa); ospf6_install_lsa (lsa); + ospf6_flood (NULL, lsa); } void @@ -208,8 +206,8 @@ ospf6_decrement_retrans_count (struct ospf6_lsa *lsa) void ospf6_install_lsa (struct ospf6_lsa *lsa) { - struct ospf6_lsa *old; struct timeval now; + struct ospf6_lsa *old; if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) @@ -222,16 +220,36 @@ ospf6_install_lsa (struct ospf6_lsa *lsa) if (old) { THREAD_OFF (old->expire); + THREAD_OFF (old->refresh); ospf6_flood_clear (old); } quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); if (! OSPF6_LSA_IS_MAXAGE (lsa)) lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa, - MAXAGE + lsa->birth.tv_sec - now.tv_sec); + OSPF_LSA_MAXAGE + lsa->birth.tv_sec - now.tv_sec); else lsa->expire = NULL; + if (OSPF6_LSA_IS_SEQWRAP(lsa) && + ! (CHECK_FLAG(lsa->flag,OSPF6_LSA_SEQWRAPPED) && + lsa->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER))) + { + if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) + zlog_debug("lsa install wrapping: sequence 0x%x", + ntohl(lsa->header->seqnum)); + SET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* in lieu of premature_aging, since we do not want to recreate this lsa + * and/or mess with timers etc, we just want to wrap the sequence number + * and reflood the lsa before continuing. + * NOTE: Flood needs to be called right after this function call, by the + * caller + */ + lsa->header->seqnum = htonl (OSPF_MAX_SEQUENCE_NUMBER); + lsa->header->age = htons (OSPF_LSA_MAXAGE); + ospf6_lsa_checksum (lsa->header); + } + /* actually install */ lsa->installed = now; ospf6_lsdb_add (lsa, lsa->lsdb); @@ -292,7 +310,7 @@ ospf6_flood_interface (struct ospf6_neighbor *from, if (ospf6_lsa_compare (lsa, req) > 0) { if (is_debug) - zlog_debug ("Requesting is newer, next neighbor"); + zlog_debug ("Requesting is older, next neighbor"); continue; } @@ -300,18 +318,30 @@ ospf6_flood_interface (struct ospf6_neighbor *from, examin next neighbor */ if (ospf6_lsa_compare (lsa, req) == 0) { - if (is_debug) - zlog_debug ("Requesting the same, remove it, next neighbor"); + if (is_debug) + zlog_debug ("Requesting the same, remove it, next neighbor"); + if (req == on->last_ls_req) + { + ospf6_lsa_unlock (req); + on->last_ls_req = NULL; + } ospf6_lsdb_remove (req, on->request_list); + ospf6_check_nbr_loading (on); continue; } /* If the new LSA is more recent, delete from request-list */ if (ospf6_lsa_compare (lsa, req) < 0) { - if (is_debug) - zlog_debug ("Received is newer, remove requesting"); + if (is_debug) + zlog_debug ("Received is newer, remove requesting"); + if (req == on->last_ls_req) + { + ospf6_lsa_unlock (req); + on->last_ls_req = NULL; + } ospf6_lsdb_remove (req, on->request_list); + ospf6_check_nbr_loading (on); /* fall through */ } } @@ -358,17 +388,22 @@ ospf6_flood_interface (struct ospf6_neighbor *from, /* (4) If the new LSA was received on this interface, and the interface state is BDR, examin next interface */ - if (from && from->ospf6_if == oi && oi->state == OSPF6_INTERFACE_BDR) + if (from && from->ospf6_if == oi) { - if (is_debug) - zlog_debug ("Received is from the I/F, itself BDR, next interface"); - return; + if (oi->state == OSPF6_INTERFACE_BDR) + { + if (is_debug) + zlog_debug ("Received is from the I/F, itself BDR, next interface"); + return; + } + SET_FLAG(lsa->flag, OSPF6_LSA_FLOODBACK); } /* (5) flood the LSA out the interface. */ if (is_debug) zlog_debug ("Schedule flooding for the interface"); - if (if_is_broadcast (oi->interface)) + if ((oi->type == OSPF_IFTYPE_BROADCAST) || + (oi->type == OSPF_IFTYPE_POINTOPOINT)) { ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsupdate_list); if (oi->thread_send_lsupdate == NULL) @@ -530,15 +565,6 @@ ospf6_acknowledge_lsa_bdrouter (struct ospf6_lsa *lsa, int ismore_recent, assert (from && from->ospf6_if); oi = from->ospf6_if; - /* LSA has been flood back out receiving interface. - No acknowledgement sent. */ - if (CHECK_FLAG (lsa->flag, OSPF6_LSA_FLOODBACK)) - { - if (is_debug) - zlog_debug ("No acknowledgement (BDR & FloodBack)"); - return; - } - /* LSA is more recent than database copy, but was not flooded back out receiving interface. Delayed acknowledgement sent if advertisement received from Designated Router, @@ -797,7 +823,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, { /* log */ if (is_debug) - zlog_debug ("Drop MaxAge LSA with direct acknowledgement."); + zlog_debug ("Drop MaxAge LSA with direct acknowledgement."); /* a) Acknowledge back to neighbor (Direct acknowledgement, 13.5) */ ospf6_lsdb_add (ospf6_lsa_copy (new), from->lsack_list); @@ -837,7 +863,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, struct timeval now, res; quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); timersub (&now, &old->installed, &res); - if (res.tv_sec < MIN_LS_ARRIVAL) + if (res.tv_sec < OSPF_MIN_LS_ARRIVAL) { if (is_debug) zlog_debug ("LSA can't be updated within MinLSArrival, discard"); @@ -849,7 +875,11 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, quagga_gettime (QUAGGA_CLK_MONOTONIC, &new->received); if (is_debug) - zlog_debug ("Flood, Install, Possibly acknowledge the received LSA"); + zlog_debug ("Install, Flood, Possibly acknowledge the received LSA"); + + /* Remove older copies of this LSA from retx lists */ + if (old) + ospf6_flood_clear (old); /* (b) immediately flood and (c) remove from all retrans-list */ /* Prevent self-originated LSA to be flooded. this is to make @@ -858,10 +888,6 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, if (new->header->adv_router != from->ospf6_if->area->ospf6->router_id) ospf6_flood (from, new); - /* (c) Remove the current database copy from all neighbors' Link - state retransmission lists. */ - /* XXX, flood_clear ? */ - /* (d), installing lsdb, which may cause routing table calculation (replacing database copy) */ ospf6_install_lsa (new); @@ -944,15 +970,15 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, /* If database copy is in 'Seqnumber Wrapping', simply discard the received LSA */ if (OSPF6_LSA_IS_MAXAGE (old) && - old->header->seqnum == htonl (MAX_SEQUENCE_NUMBER)) + old->header->seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER)) { if (is_debug) { zlog_debug ("The LSA is in Seqnumber Wrapping"); zlog_debug ("MaxAge & MaxSeqNum, discard"); } - ospf6_lsa_delete (new); - return; + ospf6_lsa_delete (new); + return; } /* Otherwise, Send database copy of this LSA to this neighbor */ @@ -969,8 +995,8 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, if (from->thread_send_lsupdate == NULL) from->thread_send_lsupdate = thread_add_event (master, ospf6_lsupdate_send_neighbor, from, 0); - ospf6_lsa_delete (new); - return; + ospf6_lsa_delete (new); + return; } return; } diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 40cda246..772caff7 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -73,14 +73,20 @@ ospf6_interface_lookup_by_ifindex (int ifindex) /* schedule routing table recalculation */ static void -ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) +ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa, unsigned int reason) { + struct ospf6_interface *oi; + + if (lsa == NULL) + return; + + oi = lsa->lsdb->data; switch (ntohs (lsa->header->type)) { case OSPF6_LSTYPE_LINK: - if (OSPF6_INTERFACE (lsa->lsdb->data)->state == OSPF6_INTERFACE_DR) - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (OSPF6_INTERFACE (lsa->lsdb->data)); - ospf6_spf_schedule (OSPF6_INTERFACE (lsa->lsdb->data)->area); + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + ospf6_spf_schedule (oi->area->ospf6, reason); break; default: @@ -88,6 +94,75 @@ ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) } } +static void +ospf6_interface_lsdb_hook_add (struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsadd_to_spf_reason(lsa)); +} + +static void +ospf6_interface_lsdb_hook_remove (struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsremove_to_spf_reason(lsa)); +} + +static u_char +ospf6_default_iftype(struct interface *ifp) +{ + if (if_is_pointopoint (ifp)) + return OSPF_IFTYPE_POINTOPOINT; + else if (if_is_loopback (ifp)) + return OSPF_IFTYPE_LOOPBACK; + else + return OSPF_IFTYPE_BROADCAST; +} + +static u_int32_t +ospf6_interface_get_cost (struct ospf6_interface *oi) +{ + /* If all else fails, use default OSPF cost */ + u_int32_t cost; + u_int32_t bw, refbw; + + bw = oi->interface->bandwidth ? oi->interface->bandwidth : OSPF6_INTERFACE_BANDWIDTH; + refbw = ospf6 ? ospf6->ref_bandwidth : OSPF6_REFERENCE_BANDWIDTH; + + /* A specifed ip ospf cost overrides a calculated one. */ + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) + cost = oi->cost; + else + { + cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5); + if (cost < 1) cost = 1; + else if (cost > UINT32_MAX) cost = UINT32_MAX; + } + + return cost; +} + +static void +ospf6_interface_recalculate_cost (struct ospf6_interface *oi) +{ + u_int32_t newcost; + + newcost = ospf6_interface_get_cost (oi); + if (newcost == oi->cost) return; + oi->cost = newcost; + + /* update cost held in route_connected list in ospf6_interface */ + ospf6_interface_connected_route_update (oi->interface); + + /* execute LSA hooks */ + if (oi->area) + { + OSPF6_LINK_LSA_SCHEDULE (oi); + OSPF6_ROUTER_LSA_SCHEDULE (oi->area); + OSPF6_NETWORK_LSA_SCHEDULE (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + } +} + /* Create new ospf6 interface structure */ struct ospf6_interface * ospf6_interface_create (struct interface *ifp) @@ -112,10 +187,10 @@ ospf6_interface_create (struct interface *ifp) oi->transdelay = OSPF6_INTERFACE_TRANSDELAY; oi->priority = OSPF6_INTERFACE_PRIORITY; - oi->hello_interval = OSPF6_INTERFACE_HELLO_INTERVAL; - oi->dead_interval = OSPF6_INTERFACE_DEAD_INTERVAL; - oi->rxmt_interval = OSPF6_INTERFACE_RXMT_INTERVAL; - oi->cost = OSPF6_INTERFACE_COST; + oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + oi->type = ospf6_default_iftype (ifp); oi->state = OSPF6_INTERFACE_DOWN; oi->flag = 0; oi->mtu_ignore = 0; @@ -134,8 +209,8 @@ ospf6_interface_create (struct interface *ifp) oi->lsupdate_list = ospf6_lsdb_create (oi); oi->lsack_list = ospf6_lsdb_create (oi); oi->lsdb = ospf6_lsdb_create (oi); - oi->lsdb->hook_add = ospf6_interface_lsdb_hook; - oi->lsdb->hook_remove = ospf6_interface_lsdb_hook; + oi->lsdb->hook_add = ospf6_interface_lsdb_hook_add; + oi->lsdb->hook_remove = ospf6_interface_lsdb_hook_remove; oi->lsdb_self = ospf6_lsdb_create (oi); oi->route_connected = OSPF6_ROUTE_TABLE_CREATE (INTERFACE, CONNECTED_ROUTES); @@ -145,6 +220,9 @@ ospf6_interface_create (struct interface *ifp) oi->interface = ifp; ifp->info = oi; + /* Compute cost. */ + oi->cost = ospf6_interface_get_cost(oi); + return oi; } @@ -189,31 +267,28 @@ void ospf6_interface_enable (struct ospf6_interface *oi) { UNSET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); - - oi->thread_send_hello = - thread_add_event (master, ospf6_hello_send, oi, 0); + ospf6_interface_state_update (oi->interface); } void ospf6_interface_disable (struct ospf6_interface *oi) { - struct listnode *node, *nnode; - struct ospf6_neighbor *on; - SET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); - for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) - ospf6_neighbor_delete (on); - - list_delete_all_node (oi->neighbor_list); + thread_execute (master, interface_down, oi, 0); ospf6_lsdb_remove_all (oi->lsdb); + ospf6_lsdb_remove_all (oi->lsdb_self); ospf6_lsdb_remove_all (oi->lsupdate_list); ospf6_lsdb_remove_all (oi->lsack_list); THREAD_OFF (oi->thread_send_hello); THREAD_OFF (oi->thread_send_lsupdate); THREAD_OFF (oi->thread_send_lsack); + + THREAD_OFF (oi->thread_network_lsa); + THREAD_OFF (oi->thread_link_lsa); + THREAD_OFF (oi->thread_intra_prefix_lsa); } static struct in6_addr * @@ -260,8 +335,7 @@ ospf6_interface_if_add (struct interface *ifp) } /* interface start */ - if (oi->area) - thread_add_event (master, interface_up, oi, 0); + ospf6_interface_state_update(oi->interface); } void @@ -297,8 +371,12 @@ ospf6_interface_state_update (struct interface *ifp) return; if (oi->area == NULL) return; + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + return; - if (if_is_up (ifp)) + if (if_is_operative (ifp) + && (ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) thread_add_event (master, interface_up, oi, 0); else thread_add_event (master, interface_down, oi, 0); @@ -325,6 +403,9 @@ ospf6_interface_connected_route_update (struct interface *ifp) if (oi->area == NULL) return; + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + return; + /* update "route to advertise" interface route table */ ospf6_route_remove_all (oi->route_connected); @@ -401,6 +482,7 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) (next_state != OSPF6_INTERFACE_DR && next_state != OSPF6_INTERFACE_BDR)) ospf6_sso (oi->interface->ifindex, &alldrouters6, IPV6_LEAVE_GROUP); + if ((prev_state != OSPF6_INTERFACE_DR && prev_state != OSPF6_INTERFACE_BDR) && (next_state == OSPF6_INTERFACE_DR || @@ -434,7 +516,7 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) } - + /* DR Election, RFC2328 section 9.4 */ #define IS_ELIGIBLE(n) \ @@ -591,7 +673,7 @@ dr_election (struct ospf6_interface *oi) return next_state; } - + /* Interface State Machine */ int interface_up (struct thread *thread) @@ -606,7 +688,7 @@ interface_up (struct thread *thread) oi->interface->name); /* check physical interface is up */ - if (! if_is_up (oi->interface)) + if (! if_is_operative (oi->interface)) { if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug ("Interface %s is down, can't execute [InterfaceUp]", @@ -614,6 +696,19 @@ interface_up (struct thread *thread) return 0; } + /* check interface has a link-local address */ + if (! (ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); + return 0; + } + + /* Recompute cost */ + ospf6_interface_recalculate_cost (oi); + /* if already enabled, do nothing */ if (oi->state > OSPF6_INTERFACE_DOWN) { @@ -623,6 +718,14 @@ interface_up (struct thread *thread) return 0; } + /* If no area assigned, return */ + if (oi->area == NULL) + { + zlog_debug ("%s: Not scheduleing Hello for %s as there is no area assigned yet", __func__, + oi->interface->name); + return 0; + } + /* Join AllSPFRouters */ ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP); @@ -631,11 +734,13 @@ interface_up (struct thread *thread) /* Schedule Hello */ if (! CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) - thread_add_event (master, ospf6_hello_send, oi, 0); + oi->thread_send_hello = thread_add_event (master, ospf6_hello_send, oi, 0); /* decide next interface state */ - if (if_is_pointopoint (oi->interface)) + if ((if_is_pointopoint (oi->interface)) || + (oi->type == OSPF_IFTYPE_POINTOPOINT)) { ospf6_interface_state_change (OSPF6_INTERFACE_POINTTOPOINT, oi); + } else if (oi->priority == 0) ospf6_interface_state_change (OSPF6_INTERFACE_DROTHER, oi); else @@ -717,6 +822,9 @@ interface_down (struct thread *thread) zlog_debug ("Interface Event %s: [InterfaceDown]", oi->interface->name); + /* Stop Hellos */ + THREAD_OFF (oi->thread_send_hello); + /* Leave AllSPFRouters */ if (oi->state > OSPF6_INTERFACE_DOWN) ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_LEAVE_GROUP); @@ -728,10 +836,14 @@ interface_down (struct thread *thread) list_delete_all_node (oi->neighbor_list); + /* When interface state is reset, also reset information about + * DR election, as it is no longer valid. */ + oi->drouter = oi->prev_drouter = htonl(0); + oi->bdrouter = oi->prev_bdrouter = htonl(0); return 0; } - + /* show specified interface structure */ static int ospf6_interface_show (struct vty *vty, struct interface *ifp) @@ -758,7 +870,7 @@ ospf6_interface_show (struct vty *vty, struct interface *ifp) type = "UNKNOWN"; vty_out (vty, "%s is %s, type %s%s", - ifp->name, updown[if_is_up (ifp)], type, + ifp->name, updown[if_is_operative (ifp)], type, VNL); vty_out (vty, " Interface ID: %d%s", ifp->ifindex, VNL); @@ -1153,20 +1265,92 @@ DEFUN (ipv6_ospf6_cost, return CMD_SUCCESS; oi->cost = lcost; - - /* update cost held in route_connected list in ospf6_interface */ - ospf6_interface_connected_route_update (oi->interface); + SET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST); - /* execute LSA hooks */ - if (oi->area) + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_cost, + no_ipv6_ospf6_cost_cmd, + "no ipv6 ospf6 cost", + NO_STR + IP6_STR + OSPF6_STR + "Calculate interface cost from bandwidth\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + UNSET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST); + + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (auto_cost_reference_bandwidth, + auto_cost_reference_bandwidth_cmd, + "auto-cost reference-bandwidth <1-4294967>", + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + struct ospf6 *o = vty->index; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + u_int32_t refbw; + + refbw = strtol (argv[0], NULL, 10); + if (refbw < 1 || refbw > 4294967) { - OSPF6_LINK_LSA_SCHEDULE (oi); - OSPF6_ROUTER_LSA_SCHEDULE (oi->area); - OSPF6_NETWORK_LSA_SCHEDULE (oi); - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + vty_out (vty, "reference-bandwidth value is invalid%s", VTY_NEWLINE); + return CMD_WARNING; } + /* If reference bandwidth is changed. */ + if ((refbw * 1000) == o->ref_bandwidth) + return CMD_SUCCESS; + + o->ref_bandwidth = refbw * 1000; + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + ospf6_interface_recalculate_cost (oi); + + return CMD_SUCCESS; +} + +DEFUN (no_auto_cost_reference_bandwidth, + no_auto_cost_reference_bandwidth_cmd, + "no auto-cost reference-bandwidth", + NO_STR + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n") +{ + struct ospf6 *o = vty->index; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + + if (o->ref_bandwidth == OSPF6_REFERENCE_BANDWIDTH) + return CMD_SUCCESS; + + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + ospf6_interface_recalculate_cost (oi); + return CMD_SUCCESS; } @@ -1292,7 +1476,10 @@ DEFUN (ipv6_ospf6_priority, oi->priority = strtol (argv[0], NULL, 10); - if (oi->area) + if (oi->area && + (oi->state == OSPF6_INTERFACE_DROTHER || + oi->state == OSPF6_INTERFACE_BDR || + oi->state == OSPF6_INTERFACE_DR)) ospf6_interface_state_change (dr_election (oi), oi); return CMD_SUCCESS; @@ -1516,6 +1703,86 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_list, return CMD_SUCCESS; } +DEFUN (ipv6_ospf6_network, + ipv6_ospf6_network_cmd, + "ipv6 ospf6 network (broadcast|point-to-point)", + IP6_STR + OSPF6_STR + "Network Type\n" + "Specify OSPFv6 broadcast network\n" + "Specify OSPF6 point-to-point network\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) { + oi = ospf6_interface_create (ifp); + } + assert (oi); + + if (strncmp (argv[0], "b", 1) == 0) + { + if (oi->type == OSPF_IFTYPE_BROADCAST) + return CMD_SUCCESS; + + oi->type = OSPF_IFTYPE_BROADCAST; + } + else if (strncmp (argv[0], "point-to-p", 10) == 0) + { + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + return CMD_SUCCESS; + } + oi->type = OSPF_IFTYPE_POINTOPOINT; + } + + /* Reset the interface */ + thread_add_event (master, interface_down, oi, 0); + thread_add_event (master, interface_up, oi, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_network, + no_ipv6_ospf6_network_cmd, + "no ipv6 ospf6 network", + NO_STR + IP6_STR + OSPF6_STR + "Network Type\n" + "Default to whatever interface type system specifies" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + int type; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) { + return CMD_SUCCESS; + } + + type = ospf6_default_iftype (ifp); + if (oi->type == type) + { + return CMD_SUCCESS; + } + oi->type = type; + + /* Reset the interface */ + thread_add_event (master, interface_down, oi, 0); + thread_add_event (master, interface_up, oi, 0); + + return CMD_SUCCESS; +} + static int config_write_ospf6_interface (struct vty *vty) { @@ -1537,7 +1804,7 @@ config_write_ospf6_interface (struct vty *vty) if (ifp->mtu6 != oi->ifmtu) vty_out (vty, " ipv6 ospf6 ifmtu %d%s", oi->ifmtu, VNL); - if (oi->cost != OSPF6_INTERFACE_COST) + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) vty_out (vty, " ipv6 ospf6 cost %d%s", oi->cost, VNL); @@ -1575,6 +1842,11 @@ config_write_ospf6_interface (struct vty *vty) if (oi->mtu_ignore) vty_out (vty, " ipv6 ospf6 mtu-ignore%s", VNL); + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + vty_out (vty, " ipv6 ospf6 network point-to-point%s", VNL); + else if (oi->type == OSPF_IFTYPE_BROADCAST) + vty_out (vty, " ipv6 ospf6 network broadcast%s", VNL); + vty_out (vty, "!%s", VNL); } return 0; @@ -1615,6 +1887,7 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &interface_desc_cmd); install_element (INTERFACE_NODE, &no_interface_desc_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_cost_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_ifmtu_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd); @@ -1632,6 +1905,13 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_advertise_prefix_list_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_network_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); + + /* reference bandwidth commands */ + install_element (OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); + install_element (OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); } DEFUN (debug_ospf6_interface, diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index d80b0730..95a377fb 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -56,6 +56,9 @@ struct ospf6_interface /* I/F transmission delay */ u_int32_t transdelay; + /* Network Type */ + u_char type; + /* Router Priority */ u_char priority; @@ -125,6 +128,7 @@ extern const char *ospf6_interface_state_str[]; /* flags */ #define OSPF6_INTERFACE_DISABLE 0x01 #define OSPF6_INTERFACE_PASSIVE 0x02 +#define OSPF6_INTERFACE_NOAUTOCOST 0x04 /* default values */ #define OSPF6_INTERFACE_HELLO_INTERVAL 10 @@ -134,8 +138,11 @@ extern const char *ospf6_interface_state_str[]; #define OSPF6_INTERFACE_PRIORITY 1 #define OSPF6_INTERFACE_TRANSDELAY 1 #define OSPF6_INTERFACE_INSTANCE_ID 0 +#define OSPF6_INTERFACE_BANDWIDTH 10000 /* Kbps */ +#define OSPF6_REFERENCE_BANDWIDTH 100000 /* Kbps */ + + - /* Function Prototypes */ extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex (int); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 9bc603b3..4ad7521e 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -46,7 +46,7 @@ #include "ospf6_abr.h" #include "ospf6_flood.h" #include "ospf6d.h" - +#include "ospf6_spf.h" unsigned char conf_debug_ospf6_brouter = 0; u_int32_t conf_debug_ospf6_brouter_specific_router_id; @@ -56,6 +56,42 @@ u_int32_t conf_debug_ospf6_brouter_specific_area_id; /* RFC2740 3.4.3.1 Router-LSA */ /******************************/ +static char * +ospf6_router_lsa_get_nbr_id (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + char *start, *end; + char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN]; + + if (lsa) + { + router_lsa = (struct ospf6_router_lsa *) + ((char *) lsa->header + sizeof (struct ospf6_lsa_header)); + start = (char *) router_lsa + sizeof (struct ospf6_router_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + + lsdesc = (struct ospf6_router_lsdesc *) + (start + pos*(sizeof (struct ospf6_router_lsdesc))); + if ((char *)lsdesc < end) + { + if (buf && (buflen > INET_ADDRSTRLEN*2)) + { + inet_ntop (AF_INET, &lsdesc->neighbor_interface_id, + buf1, sizeof(buf1)); + inet_ntop (AF_INET, &lsdesc->neighbor_router_id, + buf2, sizeof(buf2)); + sprintf (buf, "%s/%s", buf2, buf1); + } + } + else + return NULL; + } + + return buf; +} + static int ospf6_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -104,6 +140,29 @@ ospf6_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) return 0; } +int +ospf6_router_is_stub_router (struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *rtr_lsa; + + if (lsa != NULL && OSPF6_LSA_IS_TYPE (ROUTER, lsa)) + { + rtr_lsa = (struct ospf6_router_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_R)) + { + return (OSPF6_IS_STUB_ROUTER); + } + else if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_V6)) + { + return (OSPF6_IS_STUB_ROUTER_V6); + } + } + + return (OSPF6_NOT_STUB_ROUTER); +} + int ospf6_router_lsa_originate (struct thread *thread) { @@ -215,7 +274,7 @@ ospf6_router_lsa_originate (struct thread *thread) } /* Point-to-Point interfaces */ - if (if_is_pointopoint (oi->interface)) + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) { @@ -233,7 +292,7 @@ ospf6_router_lsa_originate (struct thread *thread) } /* Broadcast and NBMA interfaces */ - if (if_is_broadcast (oi->interface)) + else if (oi->type == OSPF_IFTYPE_BROADCAST) { /* If this router is not DR, and If this router not fully adjacent with DR, @@ -261,6 +320,10 @@ ospf6_router_lsa_originate (struct thread *thread) lsdesc++; } + else + { + assert (0); /* Unknown interface type */ + } /* Virtual links */ /* xxx */ @@ -268,35 +331,26 @@ ospf6_router_lsa_originate (struct thread *thread) /* xxx */ } - if ((caddr_t) lsdesc != (caddr_t) router_lsa + - sizeof (struct ospf6_router_lsa)) - { - /* Fill LSA Header */ - lsa_header->age = 0; - lsa_header->type = htons (OSPF6_LSTYPE_ROUTER); - lsa_header->id = htonl (link_state_id); - lsa_header->adv_router = oa->ospf6->router_id; - lsa_header->seqnum = - ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, - lsa_header->adv_router, oa->lsdb); - lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer); - - /* LSA checksum */ - ospf6_lsa_checksum (lsa_header); - - /* create LSA */ - lsa = ospf6_lsa_create (lsa_header); - - /* Originate */ - ospf6_lsa_originate_area (lsa, oa); - - link_state_id ++; - } - else - { - if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER)) - zlog_debug ("Nothing to describe in Router-LSA, suppress"); - } + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_ROUTER); + lsa_header->id = htonl (link_state_id); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_area (lsa, oa); + + link_state_id ++; /* Do premature-aging of rest, undesired Router-LSAs */ type = ntohs (OSPF6_LSTYPE_ROUTER); @@ -316,6 +370,36 @@ ospf6_router_lsa_originate (struct thread *thread) /* RFC2740 3.4.3.2 Network-LSA */ /*******************************/ +static char * +ospf6_network_lsa_get_ar_id (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + if (lsa) + { + network_lsa = (struct ospf6_network_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + start = (char *) network_lsa + sizeof (struct ospf6_network_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start + pos*(sizeof (struct ospf6_network_lsdesc)); + + if ((current + sizeof(struct ospf6_network_lsdesc)) <= end) + { + lsdesc = (struct ospf6_network_lsdesc *)current; + if (buf) + inet_ntop (AF_INET, &lsdesc->router_id, buf, buflen); + } + else + return NULL; + } + + return (buf); +} + static int ospf6_network_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -462,6 +546,61 @@ ospf6_network_lsa_originate (struct thread *thread) /* RFC2740 3.4.3.6 Link-LSA */ /****************************/ +static char * +ospf6_link_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + struct in6_addr in6; + struct ospf6_prefix *prefix; + int cnt = 0, prefixnum; + + if (lsa) + { + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + if (pos == 0) { + inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, buflen); + return (buf); + } + + prefixnum = ntohl (link_lsa->prefix_num); + if (pos > prefixnum) + return (NULL); + + start = (char *) link_lsa + sizeof (struct ospf6_link_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start; + + do + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + { + return (NULL); + } + + if (cnt < pos) + { + current = start + pos*OSPF6_PREFIX_SIZE(prefix); + cnt++; + } + else + { + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, buflen); + return (buf); + } + } while (current <= end); + } + return (NULL); +} + static int ospf6_link_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -614,6 +753,56 @@ ospf6_link_lsa_originate (struct thread *thread) /*****************************************/ /* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */ /*****************************************/ +static char * +ospf6_intra_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + char *start, *end, *current; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct in6_addr in6; + int prefixnum, cnt = 0; + struct ospf6_prefix *prefix; + + if (lsa) + { + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + prefixnum = ntohs (intra_prefix_lsa->prefix_num); + if (pos > prefixnum) + return (NULL); + + start = (char *) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start; + + do + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + { + return NULL; + } + + if (cnt < pos) + { + current = start + pos*OSPF6_PREFIX_SIZE(prefix); + cnt++; + } + else + { + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, buflen); + sprintf(&buf[strlen(buf)], "/%d", prefix->prefix_length); + return (buf); + } + } while (current <= end); + } + return (buf); +} static int ospf6_intra_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) @@ -1029,6 +1218,8 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) struct ospf6_prefix *op; char *start, *current, *end; char buf[64]; + struct interface *ifp; + int direct_connect = 0; if (OSPF6_LSA_IS_MAXAGE (lsa)) return; @@ -1065,6 +1256,12 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) return; } + if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) + { + /* the intra-prefix are directly connected */ + direct_connect = 1; + } + prefix_num = ntohs (intra_prefix_lsa->prefix_num); start = (caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa); @@ -1077,6 +1274,20 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) if (end < current + OSPF6_PREFIX_SIZE (op)) break; + /* Appendix A.4.1.1 */ + if (CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_NU) || + CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_LA)) + { + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + { + ospf6_linkstate_prefix2str ((struct prefix *)OSPF6_PREFIX_BODY(op), + buf, sizeof (buf)); + zlog_debug ("%s: Skipping Prefix %s has NU/LA option set", + __func__, buf); + } + continue; + } + route = ospf6_route_create (); memset (&route->prefix, 0, sizeof (struct prefix)); @@ -1095,9 +1306,18 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) route->path.cost = ls_entry->path.cost + ntohs (op->prefix_metric); - for (i = 0; ospf6_nexthop_is_set (&ls_entry->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) - ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]); + if (direct_connect) + { + ifp = if_lookup_prefix(&route->prefix); + if (ifp) + route->nexthop[0].ifindex = ifp->ifindex; + } + else + { + for (i = 0; ospf6_nexthop_is_set (&ls_entry->nexthop[i]) && + i < OSPF6_MULTI_PATH_LIMIT; i++) + ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]); + } if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) { @@ -1342,6 +1562,10 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa) ! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_B)) continue; + if (! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_V6) || + ! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_R)) + continue; + copy = ospf6_route_copy (brouter); copy->type = OSPF6_DEST_TYPE_ROUTER; copy->path.area_id = oa->area_id; @@ -1419,28 +1643,36 @@ struct ospf6_lsa_handler router_handler = { OSPF6_LSTYPE_ROUTER, "Router", - ospf6_router_lsa_show + "Rtr", + ospf6_router_lsa_show, + ospf6_router_lsa_get_nbr_id }; struct ospf6_lsa_handler network_handler = { OSPF6_LSTYPE_NETWORK, "Network", - ospf6_network_lsa_show + "Net", + ospf6_network_lsa_show, + ospf6_network_lsa_get_ar_id }; struct ospf6_lsa_handler link_handler = { OSPF6_LSTYPE_LINK, "Link", - ospf6_link_lsa_show + "Lnk", + ospf6_link_lsa_show, + ospf6_link_lsa_get_prefix_str }; struct ospf6_lsa_handler intra_prefix_handler = { OSPF6_LSTYPE_INTRA_PREFIX, "Intra-Prefix", - ospf6_intra_prefix_lsa_show + "INP", + ospf6_intra_prefix_lsa_show, + ospf6_intra_prefix_lsa_get_prefix_str }; void diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 3810174e..c9660b6a 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -94,6 +94,13 @@ struct ospf6_router_lsdesc #define OSPF6_ROUTER_LSDESC_STUB_NETWORK 3 #define OSPF6_ROUTER_LSDESC_VIRTUAL_LINK 4 +enum stub_router_mode + { + OSPF6_NOT_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER_V6, + }; + #define ROUTER_LSDESC_IS_TYPE(t,x) \ ((((struct ospf6_router_lsdesc *)(x))->type == \ OSPF6_ROUTER_LSDESC_ ## t) ? 1 : 0) @@ -146,35 +153,40 @@ struct ospf6_intra_prefix_lsa /* followed by ospf6 prefix(es) */ }; - + #define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ do { \ - if (! (oa)->thread_router_lsa) \ + if (! (oa)->thread_router_lsa \ + && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ (oa)->thread_router_lsa = \ thread_add_event (master, ospf6_router_lsa_originate, oa, 0); \ } while (0) #define OSPF6_NETWORK_LSA_SCHEDULE(oi) \ do { \ - if (! (oi)->thread_network_lsa) \ + if (! (oi)->thread_network_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_network_lsa = \ thread_add_event (master, ospf6_network_lsa_originate, oi, 0); \ } while (0) #define OSPF6_LINK_LSA_SCHEDULE(oi) \ do { \ - if (! (oi)->thread_link_lsa) \ + if (! (oi)->thread_link_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_link_lsa = \ thread_add_event (master, ospf6_link_lsa_originate, oi, 0); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \ do { \ - if (! (oa)->thread_intra_prefix_lsa) \ + if (! (oa)->thread_intra_prefix_lsa \ + && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ (oa)->thread_intra_prefix_lsa = \ thread_add_event (master, ospf6_intra_prefix_lsa_originate_stub, \ oa, 0); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \ do { \ - if (! (oi)->thread_intra_prefix_lsa) \ + if (! (oi)->thread_intra_prefix_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_intra_prefix_lsa = \ thread_add_event (master, ospf6_intra_prefix_lsa_originate_transit, \ oi, 0); \ @@ -200,6 +212,7 @@ extern char *ospf6_router_lsdesc_lookup (u_char type, u_int32_t interface_id, extern char *ospf6_network_lsdesc_lookup (u_int32_t router_id, struct ospf6_lsa *lsa); +extern int ospf6_router_is_stub_router (struct ospf6_lsa *lsa); extern int ospf6_router_lsa_originate (struct thread *); extern int ospf6_network_lsa_originate (struct thread *); extern int ospf6_link_lsa_originate (struct thread *); diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index ff061dfb..8eeb9959 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -75,7 +75,9 @@ struct ospf6_lsa_handler unknown_handler = { OSPF6_LSTYPE_UNKNOWN, "Unknown", + "Unk", ospf6_unknown_lsa_show, + NULL, OSPF6_LSA_DEBUG, }; @@ -118,6 +120,20 @@ ospf6_lstype_name (u_int16_t type) return buf; } +const char * +ospf6_lstype_short_name (u_int16_t type) +{ + static char buf[8]; + struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler (type); + if (handler && handler != &unknown_handler) + return handler->short_name; + + snprintf (buf, sizeof (buf), "0x%04hx", ntohs (type)); + return buf; +} + u_char ospf6_lstype_debug (u_int16_t type) { @@ -139,11 +155,11 @@ ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, ospf6_lsa_age_current (lsa1); ospf6_lsa_age_current (lsa2); - if (ntohs (lsa1->header->age) == MAXAGE && - ntohs (lsa2->header->age) != MAXAGE) + if (ntohs (lsa1->header->age) == OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) != OSPF_LSA_MAXAGE) return 1; - if (ntohs (lsa1->header->age) != MAXAGE && - ntohs (lsa2->header->age) == MAXAGE) + if (ntohs (lsa1->header->age) != OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) == OSPF_LSA_MAXAGE) return 1; /* compare body */ @@ -218,19 +234,19 @@ ospf6_lsa_age_current (struct ospf6_lsa *lsa) zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", safe_strerror (errno)); - if (ntohs (lsa->header->age) >= MAXAGE) + if (ntohs (lsa->header->age) >= OSPF_LSA_MAXAGE) { /* ospf6_lsa_premature_aging () sets age to MAXAGE; when using relative time, we cannot compare against lsa birth time, so we catch this special case here. */ - lsa->header->age = htons (MAXAGE); - return MAXAGE; + lsa->header->age = htons (OSPF_LSA_MAXAGE); + return OSPF_LSA_MAXAGE; } /* calculate age */ ulage = now.tv_sec - lsa->birth.tv_sec; /* if over MAXAGE, set to it */ - age = (ulage > MAXAGE ? MAXAGE : ulage); + age = (ulage > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage); lsa->header->age = htons (age); return age; @@ -243,8 +259,8 @@ ospf6_lsa_age_update_to_send (struct ospf6_lsa *lsa, u_int32_t transdelay) unsigned short age; age = ospf6_lsa_age_current (lsa) + transdelay; - if (age > MAXAGE) - age = MAXAGE; + if (age > OSPF_LSA_MAXAGE) + age = OSPF_LSA_MAXAGE; lsa->header->age = htons (age); } @@ -258,7 +274,30 @@ ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) THREAD_OFF (lsa->expire); THREAD_OFF (lsa->refresh); - lsa->header->age = htons (MAXAGE); + /* + * We clear the LSA from the neighbor retx lists now because it + * will not get deleted later. Essentially, changing the age to + * MaxAge will prevent this LSA from being matched with its + * existing entries in the retx list thereby causing those entries + * to be silently replaced with its MaxAged version, but with ever + * increasing retx count causing this LSA to remain forever and + * for the MaxAge remover thread to be called forever too. + * + * The reason the previous entry silently disappears is that when + * entry is added to a neighbor's retx list, it replaces the existing + * entry. But since the ospf6_lsdb_add() routine is generic and not aware + * of the special semantics of retx count, the retx count is not + * decremented when its replaced. Attempting to add the incr and decr + * retx count routines as the hook_add and hook_remove for the retx lists + * have a problem because the hook_remove routine is called for MaxAge + * entries (as will be the case in a traditional LSDB, unlike in this case + * where an LSDB is used as an efficient tree structure to store all kinds + * of data) that are added instead of calling the hook_add routine. + */ + + ospf6_flood_clear (lsa); + + lsa->header->age = htons (OSPF_LSA_MAXAGE); thread_execute (master, ospf6_lsa_expire, lsa, 0); } @@ -267,7 +306,7 @@ ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) int ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) { - int seqnuma, seqnumb; + int32_t seqnuma, seqnumb; u_int16_t cksuma, cksumb; u_int16_t agea, ageb; @@ -275,8 +314,8 @@ ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) assert (b && b->header); assert (OSPF6_LSA_IS_SAME (a, b)); - seqnuma = (int) ntohl (a->header->seqnum); - seqnumb = (int) ntohl (b->header->seqnum); + seqnuma = (int32_t) ntohl (a->header->seqnum); + seqnumb = (int32_t) ntohl (b->header->seqnum); /* compare by sequence number */ if (seqnuma > seqnumb) @@ -297,15 +336,15 @@ ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) ageb = ospf6_lsa_age_current (b); /* MaxAge check */ - if (agea == MAXAGE && ageb != MAXAGE) + if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE) return -1; - else if (agea != MAXAGE && ageb == MAXAGE) + else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE) return 1; /* Age check */ - if (agea > ageb && agea - ageb >= MAX_AGE_DIFF) + if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF) return 1; - else if (agea < ageb && ageb - agea >= MAX_AGE_DIFF) + else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF) return -1; /* neither recent */ @@ -348,17 +387,19 @@ ospf6_lsa_header_print (struct ospf6_lsa *lsa) void ospf6_lsa_show_summary_header (struct vty *vty) { - vty_out (vty, "%-12s %-15s %-15s %4s %8s %4s %4s %-8s%s", + vty_out (vty, "%-4s %-15s%-15s%4s %8s %30s%s", "Type", "LSId", "AdvRouter", "Age", "SeqNum", - "Cksm", "Len", "Duration", VNL); + "Payload", VNL); } void ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) { char adv_router[16], id[16]; - struct timeval now, res; - char duration[16]; + int type; + struct ospf6_lsa_handler *handler; + char buf[64], tmpbuf[80]; + int cnt = 0; assert (lsa); assert (lsa->header); @@ -367,16 +408,38 @@ ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, sizeof (adv_router)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - timersub (&now, &lsa->installed, &res); - timerstring (&res, duration, sizeof (duration)); - - vty_out (vty, "%-12s %-15s %-15s %4hu %8lx %04hx %4hu %8s%s", - ospf6_lstype_name (lsa->header->type), - id, adv_router, ospf6_lsa_age_current (lsa), - (u_long) ntohl (lsa->header->seqnum), - ntohs (lsa->header->checksum), ntohs (lsa->header->length), - duration, VNL); + type = ntohs(lsa->header->type); + handler = ospf6_get_lsa_handler (lsa->header->type); + if ((type == OSPF6_LSTYPE_INTER_PREFIX) || + (type == OSPF6_LSTYPE_INTER_ROUTER) || + (type == OSPF6_LSTYPE_AS_EXTERNAL)) + { + vty_out (vty, "%-4s %-15s%-15s%4hu %8lx %30s%s", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum), + handler->get_prefix_str(lsa, buf, sizeof(buf), 0), VNL); + } + else if (type != OSPF6_LSTYPE_UNKNOWN) + { + sprintf (tmpbuf, "%-4s %-15s%-15s%4hu %8lx", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum)); + + while (handler->get_prefix_str(lsa, buf, sizeof(buf), cnt) != NULL) + { + vty_out (vty, "%s %30s%s", tmpbuf, buf, VNL); + cnt++; + } + } + else + { + vty_out (vty, "%-4s %-15s%-15s%4hu %8lx%s", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum), VNL); + } } void @@ -427,8 +490,11 @@ ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, "CheckSum: %#06hx Length: %hu%s", ntohs (lsa->header->checksum), ntohs (lsa->header->length), VNL); - vty_out (vty, " Prev: %p This: %p Next: %p%s", - lsa->prev, lsa, lsa->next, VNL); + vty_out (vty, "Flag: %x %s", lsa->flag, VNL); + vty_out (vty, "Lock: %d %s", lsa->lock, VNL); + vty_out (vty, "ReTx Count: %d%s", lsa->retrans_count, VNL); + vty_out (vty, "Threads: Expire: 0x%p, Refresh: 0x%p %s", + lsa->expire, lsa->refresh, VNL); vty_out (vty, "%s", VNL); return; } @@ -438,6 +504,8 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { char adv_router[64], id[64]; struct ospf6_lsa_handler *handler; + struct timeval now, res; + char duration[16]; assert (lsa && lsa->header); @@ -445,6 +513,10 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, sizeof (adv_router)); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &lsa->installed, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa), ospf6_lstype_name (lsa->header->type), VNL); vty_out (vty, "Link State ID: %s%s", id, VNL); @@ -454,6 +526,7 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, "CheckSum: %#06hx Length: %hu%s", ntohs (lsa->header->checksum), ntohs (lsa->header->length), VNL); + vty_out (vty, "Duration: %s%s", duration, VNL); handler = ospf6_get_lsa_handler (lsa->header->type); if (handler->show == NULL) @@ -558,6 +631,7 @@ ospf6_lsa_copy (struct ospf6_lsa *lsa) copy->received = lsa->received; copy->installed = lsa->installed; copy->lsdb = lsa->lsdb; + copy->rn = NULL; return copy; } @@ -584,7 +658,7 @@ ospf6_lsa_unlock (struct ospf6_lsa *lsa) ospf6_lsa_delete (lsa); } - + /* ospf6 lsa expiry */ int ospf6_lsa_expire (struct thread *thread) @@ -608,12 +682,12 @@ ospf6_lsa_expire (struct thread *thread) if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY)) return 0; /* dbexchange will do something ... */ - /* reflood lsa */ - ospf6_flood (NULL, lsa); - /* reinstall lsa */ ospf6_install_lsa (lsa); + /* reflood lsa */ + ospf6_flood (NULL, lsa); + /* schedule maxage remover */ ospf6_maxage_remove (ospf6); @@ -653,7 +727,7 @@ ospf6_lsa_refresh (struct thread *thread) new = ospf6_lsa_create (self->header); new->lsdb = old->lsdb; new->refresh = thread_add_timer (master, ospf6_lsa_refresh, new, - LS_REFRESH_TIME); + OSPF_LS_REFRESH_TIME); /* store it in the LSDB for self-originated LSAs */ ospf6_lsdb_add (ospf6_lsa_copy (new), lsdb_self); @@ -664,14 +738,13 @@ ospf6_lsa_refresh (struct thread *thread) ospf6_lsa_header_print (new); } - ospf6_flood_clear (old); - ospf6_flood (NULL, new); ospf6_install_lsa (new); + ospf6_flood (NULL, new); return 0; } - + /* Fletcher Checksum -- Refer to RFC1008. */ @@ -717,7 +790,7 @@ ospf6_lsa_terminate (void) { vector_free (ospf6_lsa_handler_vector); } - + static char * ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) { @@ -725,7 +798,7 @@ ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) unsigned int i; unsigned int size = strlen (h->name); - if (!strcmp(h->name, "Unknown") && + if (!strcmp(h->name, "unknown") && h->type != OSPF6_LSTYPE_UNKNOWN) { snprintf (buf, sizeof (buf), "%#04hx", h->type); @@ -745,7 +818,7 @@ ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) DEFUN (debug_ospf6_lsa_type, debug_ospf6_lsa_hex_cmd, - "debug ospf6 lsa XXXX/0xXXXX", + "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)", DEBUG_STR OSPF6_STR "Debug Link State Advertisements (LSAs)\n" @@ -754,44 +827,21 @@ DEFUN (debug_ospf6_lsa_type, { unsigned int i; struct ospf6_lsa_handler *handler = NULL; - unsigned long val; - char *endptr = NULL; - u_int16_t type = 0; assert (argc); - if ((strlen (argv[0]) == 6 && ! strncmp (argv[0], "0x", 2)) || - (strlen (argv[0]) == 4)) - { - val = strtoul (argv[0], &endptr, 16); - if (*endptr == '\0') - type = val; - } - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) { handler = vector_slot (ospf6_lsa_handler_vector, i); if (handler == NULL) continue; - if (type && handler->type == type) + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) break; if (! strcasecmp (argv[0], handler->name)) break; handler = NULL; } - if (type && handler == NULL) - { - handler = (struct ospf6_lsa_handler *) - malloc (sizeof (struct ospf6_lsa_handler)); - memset (handler, 0, sizeof (struct ospf6_lsa_handler)); - handler->type = type; - handler->name = "Unknown"; - handler->show = ospf6_unknown_lsa_show; - vector_set_index (ospf6_lsa_handler_vector, - handler->type & OSPF6_LSTYPE_FCODE_MASK, handler); - } - if (handler == NULL) handler = &unknown_handler; @@ -799,7 +849,7 @@ DEFUN (debug_ospf6_lsa_type, { if (! strcmp (argv[1], "originate")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); - if (! strcmp (argv[1], "examin")) + if (! strcmp (argv[1], "examine")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN); if (! strcmp (argv[1], "flooding")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD); @@ -810,9 +860,18 @@ DEFUN (debug_ospf6_lsa_type, return CMD_SUCCESS; } +ALIAS (debug_ospf6_lsa_type, + debug_ospf6_lsa_hex_detail_cmd, + "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown) (originate|examine|flooding)", + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Specify LS type as Hexadecimal\n" + ) + DEFUN (no_debug_ospf6_lsa_type, no_debug_ospf6_lsa_hex_cmd, - "no debug ospf6 lsa XXXX/0xXXXX", + "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)", NO_STR DEBUG_STR OSPF6_STR @@ -822,26 +881,15 @@ DEFUN (no_debug_ospf6_lsa_type, { u_int i; struct ospf6_lsa_handler *handler = NULL; - unsigned long val; - char *endptr = NULL; - u_int16_t type = 0; assert (argc); - if ((strlen (argv[0]) == 6 && ! strncmp (argv[0], "0x", 2)) || - (strlen (argv[0]) == 4)) - { - val = strtoul (argv[0], &endptr, 16); - if (*endptr == '\0') - type = val; - } - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) { handler = vector_slot (ospf6_lsa_handler_vector, i); if (handler == NULL) continue; - if (type && handler->type == type) + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) break; if (! strcasecmp (argv[0], handler->name)) break; @@ -854,7 +902,7 @@ DEFUN (no_debug_ospf6_lsa_type, { if (! strcmp (argv[1], "originate")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); - if (! strcmp (argv[1], "examin")) + if (! strcmp (argv[1], "examine")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN); if (! strcmp (argv[1], "flooding")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD); @@ -862,120 +910,30 @@ DEFUN (no_debug_ospf6_lsa_type, else UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG); - if (handler->debug == 0 && - !strcmp(handler->name, "Unknown") && type != OSPF6_LSTYPE_UNKNOWN) - { - free (handler); - vector_slot (ospf6_lsa_handler_vector, i) = NULL; - } - return CMD_SUCCESS; } -struct cmd_element debug_ospf6_lsa_type_cmd; -struct cmd_element debug_ospf6_lsa_type_detail_cmd; -struct cmd_element no_debug_ospf6_lsa_type_cmd; -struct cmd_element no_debug_ospf6_lsa_type_detail_cmd; +ALIAS (no_debug_ospf6_lsa_type, + no_debug_ospf6_lsa_hex_detail_cmd, + "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix) (originate|examine|flooding)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Specify LS type as Hexadecimal\n" + ) void install_element_ospf6_debug_lsa (void) { - u_int i; - struct ospf6_lsa_handler *handler; -#define STRSIZE 256 -#define DOCSIZE 1024 - static char strbuf[STRSIZE]; - static char docbuf[DOCSIZE]; - static char detail_strbuf[STRSIZE]; - static char detail_docbuf[DOCSIZE]; - char *str, *no_str; - char *doc, *no_doc; - - strbuf[0] = '\0'; - no_str = &strbuf[strlen (strbuf)]; - strncat (strbuf, "no ", STRSIZE - strlen (strbuf)); - str = &strbuf[strlen (strbuf)]; - - strncat (strbuf, "debug ospf6 lsa (", STRSIZE - strlen (strbuf)); - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) - { - handler = vector_slot (ospf6_lsa_handler_vector, i); - if (handler == NULL) - continue; - strncat (strbuf, ospf6_lsa_handler_name (handler), - STRSIZE - strlen (strbuf)); - strncat (strbuf, "|", STRSIZE - strlen (strbuf)); - } - strbuf[strlen (strbuf) - 1] = ')'; - strbuf[strlen (strbuf)] = '\0'; - - docbuf[0] = '\0'; - no_doc = &docbuf[strlen (docbuf)]; - strncat (docbuf, NO_STR, DOCSIZE - strlen (docbuf)); - doc = &docbuf[strlen (docbuf)]; - - strncat (docbuf, DEBUG_STR, DOCSIZE - strlen (docbuf)); - strncat (docbuf, OSPF6_STR, DOCSIZE - strlen (docbuf)); - strncat (docbuf, "Debug Link State Advertisements (LSAs)\n", - DOCSIZE - strlen (docbuf)); - - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) - { - handler = vector_slot (ospf6_lsa_handler_vector, i); - if (handler == NULL) - continue; - strncat (docbuf, "Debug ", DOCSIZE - strlen (docbuf)); - strncat (docbuf, handler->name, DOCSIZE - strlen (docbuf)); - strncat (docbuf, "-LSA\n", DOCSIZE - strlen (docbuf)); - } - docbuf[strlen (docbuf)] = '\0'; - - debug_ospf6_lsa_type_cmd.string = str; - debug_ospf6_lsa_type_cmd.func = debug_ospf6_lsa_type; - debug_ospf6_lsa_type_cmd.doc = doc; - - no_debug_ospf6_lsa_type_cmd.string = no_str; - no_debug_ospf6_lsa_type_cmd.func = no_debug_ospf6_lsa_type; - no_debug_ospf6_lsa_type_cmd.doc = no_doc; - - strncpy (detail_strbuf, strbuf, STRSIZE); - strncat (detail_strbuf, " (originate|examin|flooding)", - STRSIZE - strlen (detail_strbuf)); - detail_strbuf[strlen (detail_strbuf)] = '\0'; - no_str = &detail_strbuf[0]; - str = &detail_strbuf[strlen ("no ")]; - - strncpy (detail_docbuf, docbuf, DOCSIZE); - strncat (detail_docbuf, "Debug Originating LSA\n", - DOCSIZE - strlen (detail_docbuf)); - strncat (detail_docbuf, "Debug Examining LSA\n", - DOCSIZE - strlen (detail_docbuf)); - strncat (detail_docbuf, "Debug Flooding LSA\n", - DOCSIZE - strlen (detail_docbuf)); - detail_docbuf[strlen (detail_docbuf)] = '\0'; - no_doc = &detail_docbuf[0]; - doc = &detail_docbuf[strlen (NO_STR)]; - - debug_ospf6_lsa_type_detail_cmd.string = str; - debug_ospf6_lsa_type_detail_cmd.func = debug_ospf6_lsa_type; - debug_ospf6_lsa_type_detail_cmd.doc = doc; - - no_debug_ospf6_lsa_type_detail_cmd.string = no_str; - no_debug_ospf6_lsa_type_detail_cmd.func = no_debug_ospf6_lsa_type; - no_debug_ospf6_lsa_type_detail_cmd.doc = no_doc; - install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_cmd); - install_element (ENABLE_NODE, &debug_ospf6_lsa_type_cmd); - install_element (ENABLE_NODE, &debug_ospf6_lsa_type_detail_cmd); + install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_detail_cmd); install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); - install_element (ENABLE_NODE, &no_debug_ospf6_lsa_type_cmd); - install_element (ENABLE_NODE, &no_debug_ospf6_lsa_type_detail_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_detail_cmd); install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); - install_element (CONFIG_NODE, &debug_ospf6_lsa_type_cmd); - install_element (CONFIG_NODE, &debug_ospf6_lsa_type_detail_cmd); + install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_detail_cmd); install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd); - install_element (CONFIG_NODE, &no_debug_ospf6_lsa_type_cmd); - install_element (CONFIG_NODE, &no_debug_ospf6_lsa_type_detail_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_detail_cmd); } int @@ -996,7 +954,7 @@ config_write_ospf6_debug_lsa (struct vty *vty) vty_out (vty, "debug ospf6 lsa %s originate%s", ospf6_lsa_handler_name (handler), VNL); if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN)) - vty_out (vty, "debug ospf6 lsa %s examin%s", + vty_out (vty, "debug ospf6 lsa %s examine%s", ospf6_lsa_handler_name (handler), VNL); if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD)) vty_out (vty, "debug ospf6 lsa %s flooding%s", diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 263411fc..a85ca66d 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -107,15 +107,16 @@ struct ospf6_lsa_header ((L)->header->adv_router == (a) && (L)->header->id == (i) && \ (L)->header->type == (t)) #define OSPF6_LSA_IS_DIFFER(L1, L2) ospf6_lsa_is_differ (L1, L2) -#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == MAXAGE) +#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == OSPF_LSA_MAXAGE) #define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2) +#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1)) + struct ospf6_lsa { char name[64]; /* dump string */ - struct ospf6_lsa *prev; - struct ospf6_lsa *next; + struct route_node *rn; unsigned char lock; /* reference counter */ unsigned char flag; /* special meaning (e.g. floodback) */ @@ -140,12 +141,15 @@ struct ospf6_lsa #define OSPF6_LSA_FLOODBACK 0x02 #define OSPF6_LSA_DUPLICATE 0x04 #define OSPF6_LSA_IMPLIEDACK 0x08 +#define OSPF6_LSA_SEQWRAPPED 0x20 struct ospf6_lsa_handler { u_int16_t type; /* host byte order */ const char *name; + const char *short_name; int (*show) (struct vty *, struct ospf6_lsa *); + char *(*get_prefix_str) (struct ospf6_lsa *, char *buf, int buflen, int pos); u_char debug; }; @@ -205,9 +209,10 @@ extern struct ospf6_lsa_handler unknown_handler; continue; \ } - + /* Function Prototypes */ extern const char *ospf6_lstype_name (u_int16_t type); +extern const char *ospf6_lstype_short_name (u_int16_t type); extern u_char ospf6_lstype_debug (u_int16_t type); extern int ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern int ospf6_lsa_is_changed (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 280bdf95..b5a4587c 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -54,9 +54,12 @@ ospf6_lsdb_create (void *data) void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb) { - ospf6_lsdb_remove_all (lsdb); - route_table_finish (lsdb->table); - XFREE (MTYPE_OSPF6_LSDB, lsdb); + if (lsdb != NULL) + { + ospf6_lsdb_remove_all (lsdb); + route_table_finish (lsdb->table); + XFREE (MTYPE_OSPF6_LSDB, lsdb); + } } static void @@ -70,7 +73,7 @@ ospf6_lsdb_set_key (struct prefix_ipv6 *key, void *value, int len) key->prefixlen += len * 8; } -#ifndef NDEBUG +#ifdef DEBUG static void _lsdb_count_assert (struct ospf6_lsdb *lsdb) { @@ -94,16 +97,16 @@ _lsdb_count_assert (struct ospf6_lsdb *lsdb) assert (num == lsdb->count); } #define ospf6_lsdb_count_assert(t) (_lsdb_count_assert (t)) -#else /*NDEBUG*/ +#else /*DEBUG*/ #define ospf6_lsdb_count_assert(t) ((void) 0) -#endif /*NDEBUG*/ +#endif /*DEBUG*/ void ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) { struct prefix_ipv6 key; - struct route_node *current, *nextnode, *prevnode; - struct ospf6_lsa *next, *prev, *old = NULL; + struct route_node *current; + struct ospf6_lsa *old = NULL; memset (&key, 0, sizeof (key)); ospf6_lsdb_set_key (&key, &lsa->header->type, sizeof (lsa->header->type)); @@ -114,55 +117,25 @@ ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) current = route_node_get (lsdb->table, (struct prefix *) &key); old = current->info; current->info = lsa; + lsa->rn = current; ospf6_lsa_lock (lsa); - if (old) - { - if (old->prev) - old->prev->next = lsa; - if (old->next) - old->next->prev = lsa; - lsa->next = old->next; - lsa->prev = old->prev; - } - else + if (!old) { - /* next link */ - nextnode = current; - route_lock_node (nextnode); - do { - nextnode = route_next (nextnode); - } while (nextnode && nextnode->info == NULL); - if (nextnode == NULL) - lsa->next = NULL; - else - { - next = nextnode->info; - lsa->next = next; - next->prev = lsa; - route_unlock_node (nextnode); - } + lsdb->count++; - /* prev link */ - prevnode = current; - route_lock_node (prevnode); - do { - prevnode = route_prev (prevnode); - } while (prevnode && prevnode->info == NULL); - if (prevnode == NULL) - lsa->prev = NULL; + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + if (lsdb->hook_remove) + (*lsdb->hook_remove) (lsa); + } else - { - prev = prevnode->info; - lsa->prev = prev; - prev->next = lsa; - route_unlock_node (prevnode); - } - - lsdb->count++; + { + if (lsdb->hook_add) + (*lsdb->hook_add) (lsa); + } } - - if (old) + else { if (OSPF6_LSA_IS_CHANGED (old, lsa)) { @@ -187,20 +160,8 @@ ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) (*lsdb->hook_add) (lsa); } } + ospf6_lsa_unlock (old); } - else if (OSPF6_LSA_IS_MAXAGE (lsa)) - { - if (lsdb->hook_remove) - (*lsdb->hook_remove) (lsa); - } - else - { - if (lsdb->hook_add) - (*lsdb->hook_add) (lsa); - } - - if (old) - ospf6_lsa_unlock (old); ospf6_lsdb_count_assert (lsdb); } @@ -220,19 +181,15 @@ ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) node = route_node_lookup (lsdb->table, (struct prefix *) &key); assert (node && node->info == lsa); - if (lsa->prev) - lsa->prev->next = lsa->next; - if (lsa->next) - lsa->next->prev = lsa->prev; - node->info = NULL; lsdb->count--; if (lsdb->hook_remove) (*lsdb->hook_remove) (lsa); + route_unlock_node (node); /* to free the lookup lock */ + route_unlock_node (node); /* to free the original lock */ ospf6_lsa_unlock (lsa); - route_unlock_node (node); ospf6_lsdb_count_assert (lsdb); } @@ -255,6 +212,8 @@ ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, u_int32_t adv_router, node = route_node_lookup (lsdb->table, (struct prefix *) &key); if (node == NULL || node->info == NULL) return NULL; + + route_unlock_node (node); return (struct ospf6_lsa *) node->info; } @@ -306,21 +265,9 @@ ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id, u_int32_t adv_router, if (prefix_same (&node->p, p)) { - struct route_node *prev = node; - struct ospf6_lsa *lsa_prev; - struct ospf6_lsa *lsa_next; - node = route_next (node); while (node && node->info == NULL) node = route_next (node); - - lsa_prev = prev->info; - lsa_next = (node ? node->info : NULL); - assert (lsa_prev); - assert (lsa_prev->next == lsa_next); - if (lsa_next) - assert (lsa_next->prev == lsa_prev); - zlog_debug ("lsdb_lookup_next: assert OK with previous LSA"); } if (! node) @@ -346,7 +293,6 @@ ospf6_lsdb_head (struct ospf6_lsdb *lsdb) if (node == NULL) return NULL; - route_unlock_node (node); if (node->info) ospf6_lsa_lock ((struct ospf6_lsa *) node->info); return (struct ospf6_lsa *) node->info; @@ -355,12 +301,20 @@ ospf6_lsdb_head (struct ospf6_lsdb *lsdb) struct ospf6_lsa * ospf6_lsdb_next (struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct route_node *node = lsa->rn; + struct ospf6_lsa *next = NULL; - ospf6_lsa_unlock (lsa); - if (next) - ospf6_lsa_lock (next); + do { + node = route_next (node); + } while (node && node->info == NULL); + + if ((node != NULL) && (node->info != NULL)) + { + next = node->info; + ospf6_lsa_lock (next); + } + ospf6_lsa_unlock (lsa); return next; } @@ -390,8 +344,6 @@ ospf6_lsdb_type_router_head (u_int16_t type, u_int32_t adv_router, if (node == NULL) return NULL; - else - route_unlock_node (node); if (! prefix_match ((struct prefix *) &key, &node->p)) return NULL; @@ -406,18 +358,19 @@ struct ospf6_lsa * ospf6_lsdb_type_router_next (u_int16_t type, u_int32_t adv_router, struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct ospf6_lsa *next = ospf6_lsdb_next(lsa); if (next) { if (next->header->type != type || next->header->adv_router != adv_router) - next = NULL; + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } } - if (next) - ospf6_lsa_lock (next); - ospf6_lsa_unlock (lsa); return next; } @@ -444,8 +397,6 @@ ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb) if (node == NULL) return NULL; - else - route_unlock_node (node); if (! prefix_match ((struct prefix *) &key, &node->p)) return NULL; @@ -459,17 +410,18 @@ ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb) struct ospf6_lsa * ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct ospf6_lsa *next = ospf6_lsdb_next (lsa); if (next) { if (next->header->type != type) - next = NULL; + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } } - if (next) - ospf6_lsa_lock (next); - ospf6_lsa_unlock (lsa); return next; } @@ -477,27 +429,85 @@ void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb) { struct ospf6_lsa *lsa; + + if (lsdb == NULL) + return; + for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) ospf6_lsdb_remove (lsa, lsdb); } void -ospf6_lsdb_show (struct vty *vty, int level, +ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa) +{ + if (lsa != NULL) + { + if (lsa->rn != NULL) + route_unlock_node (lsa->rn); + ospf6_lsa_unlock (lsa); + } +} + +int +ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) +{ + int reschedule = 0; + struct ospf6_lsa *lsa; + + for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) + { + if (! OSPF6_LSA_IS_MAXAGE (lsa)) + continue; + if (lsa->retrans_count != 0) + { + reschedule = 1; + continue; + } + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) + zlog_debug ("Remove MaxAge %s", lsa->name); + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED)) + { + UNSET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* + * lsa->header->age = 0; + */ + lsa->header->seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER + 1); + ospf6_lsa_checksum (lsa->header); + + THREAD_OFF(lsa->refresh); + thread_execute (master, ospf6_lsa_refresh, lsa, 0); + } else { + ospf6_lsdb_remove (lsa, lsdb); + } + } + + return (reschedule); +} + +void +ospf6_lsdb_show (struct vty *vty, enum ospf_lsdb_show_level level, u_int16_t *type, u_int32_t *id, u_int32_t *adv_router, struct ospf6_lsdb *lsdb) { struct ospf6_lsa *lsa; void (*showfunc) (struct vty *, struct ospf6_lsa *) = NULL; - if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) - showfunc = ospf6_lsa_show_summary; - else if (level == OSPF6_LSDB_SHOW_LEVEL_DETAIL) - showfunc = ospf6_lsa_show; - else if (level == OSPF6_LSDB_SHOW_LEVEL_INTERNAL) - showfunc = ospf6_lsa_show_internal; - else if (level == OSPF6_LSDB_SHOW_LEVEL_DUMP) - showfunc = ospf6_lsa_show_dump; - + switch (level) + { + case OSPF6_LSDB_SHOW_LEVEL_DETAIL: + showfunc = ospf6_lsa_show; + break; + case OSPF6_LSDB_SHOW_LEVEL_INTERNAL: + showfunc = ospf6_lsa_show_internal; + break; + case OSPF6_LSDB_SHOW_LEVEL_DUMP: + showfunc = ospf6_lsa_show_dump; + break; + case OSPF6_LSDB_SHOW_LEVEL_NORMAL: + default: + showfunc = ospf6_lsa_show_summary; + } + if (type && id && adv_router) { lsa = ospf6_lsdb_lookup (*type, *id, *adv_router, lsdb); @@ -551,7 +561,7 @@ ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, continue; if (ntohl (lsa->header->id) > id) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } id++; @@ -572,7 +582,7 @@ ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router, /* if current database copy not found, return InitialSequenceNumber */ lsa = ospf6_lsdb_lookup (type, id, adv_router, lsdb); if (lsa == NULL) - seqnum = INITIAL_SEQUENCE_NUMBER; + seqnum = OSPF_INITIAL_SEQUENCE_NUMBER; else seqnum = (signed long) ntohl (lsa->header->seqnum) + 1; diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 71297dae..425f6153 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -34,21 +34,6 @@ struct ospf6_lsdb void (*hook_remove) (struct ospf6_lsa *); }; -#define OSPF6_LSDB_MAXAGE_REMOVER(lsdb) \ - do { \ - struct ospf6_lsa *lsa; \ - for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) \ - { \ - if (! OSPF6_LSA_IS_MAXAGE (lsa)) \ - continue; \ - if (lsa->retrans_count != 0) \ - continue; \ - if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) \ - zlog_debug ("Remove MaxAge %s", lsa->name); \ - ospf6_lsdb_remove (lsa, lsdb); \ - } \ - } while (0) - /* Function Prototypes */ extern struct ospf6_lsdb *ospf6_lsdb_create (void *data); extern void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb); @@ -79,13 +64,17 @@ extern struct ospf6_lsa *ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa); extern void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb); +extern void ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa); -#define OSPF6_LSDB_SHOW_LEVEL_NORMAL 0 -#define OSPF6_LSDB_SHOW_LEVEL_DETAIL 1 -#define OSPF6_LSDB_SHOW_LEVEL_INTERNAL 2 -#define OSPF6_LSDB_SHOW_LEVEL_DUMP 3 +enum ospf_lsdb_show_level { + OSPF6_LSDB_SHOW_LEVEL_NORMAL = 0, + OSPF6_LSDB_SHOW_LEVEL_DETAIL, + OSPF6_LSDB_SHOW_LEVEL_INTERNAL, + OSPF6_LSDB_SHOW_LEVEL_DUMP, +}; -extern void ospf6_lsdb_show (struct vty *vty, int level, u_int16_t *type, +extern void ospf6_lsdb_show (struct vty *vty, + enum ospf_lsdb_show_level level, u_int16_t *type, u_int32_t *id, u_int32_t *adv_router, struct ospf6_lsdb *lsdb); @@ -94,5 +83,6 @@ extern u_int32_t ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, extern u_int32_t ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router, struct ospf6_lsdb *lsdb); +extern int ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb); #endif /* OSPF6_LSDB_H */ diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 1bdf52fb..4f6d9e51 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -41,6 +41,8 @@ #include "ospf6_message.h" #include "ospf6_asbr.h" #include "ospf6_lsa.h" +#include "ospf6_interface.h" +#include "ospf6_zebra.h" /* Default configuration file name for ospf6d. */ #define OSPF6_DEFAULT_CONFIG "ospf6d.conf" @@ -125,7 +127,7 @@ Daemon which manages OSPF version 3.\n\n\ -C, --dryrun Check configuration for validity and exit\n\ -h, --help Display this help and exit\n\ \n\ -Report bugs to zebra@zebra.org\n", progname); +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); @@ -134,12 +136,16 @@ Report bugs to zebra@zebra.org\n", progname); static void __attribute__ ((noreturn)) ospf6_exit (int status) { - extern struct ospf6 *ospf6; - extern struct zclient *zclient; + struct listnode *node; + struct interface *ifp; if (ospf6) ospf6_delete (ospf6); + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + if (ifp->info != NULL) + ospf6_interface_delete(ifp->info); + ospf6_message_terminate (); ospf6_asbr_terminate (); ospf6_lsa_terminate (); @@ -319,9 +325,6 @@ main (int argc, char *argv[], char *envp[]) /* initialize ospf6 */ ospf6_init (); - /* sort command vector */ - sort_node (); - /* parse config file */ vty_read_config (config_file, config_default); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 5edb70ce..ecc96f71 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -517,20 +517,20 @@ ospf6_dbdesc_recv_master (struct ospf6_header *oh, { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request (No database copy)"); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } else if (ospf6_lsa_compare (his, mine) < 0) { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request (Received MoreRecent)"); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } else { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Discard (Existing MoreRecent)"); - ospf6_lsa_delete (his); } + ospf6_lsa_delete (his); } assert (p == OSPF6_MESSAGE_END (oh)); @@ -539,7 +539,7 @@ ospf6_dbdesc_recv_master (struct ospf6_header *oh, on->dbdesc_seqnum ++; /* schedule send lsreq */ - if (on->thread_send_lsreq == NULL) + if (on->request_list->count && (on->thread_send_lsreq == NULL)) on->thread_send_lsreq = thread_add_event (master, ospf6_lsreq_send, on, 0); @@ -735,10 +735,9 @@ ospf6_dbdesc_recv_slave (struct ospf6_header *oh, { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request-list: %s", his->name); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } - else - ospf6_lsa_delete (his); + ospf6_lsa_delete (his); } assert (p == OSPF6_MESSAGE_END (oh)); @@ -747,7 +746,8 @@ ospf6_dbdesc_recv_slave (struct ospf6_header *oh, on->dbdesc_seqnum = ntohl (dbdesc->seqnum); /* schedule send lsreq */ - if (on->thread_send_lsreq == NULL) + if ((on->thread_send_lsreq == NULL) && + (on->request_list->count)) on->thread_send_lsreq = thread_add_event (master, ospf6_lsreq_send, on, 0); @@ -1282,7 +1282,7 @@ ospf6_rxpacket_examin (struct ospf6_interface *oi, struct ospf6_header *oh, cons { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) { - if (oh->area_id == BACKBONE_AREA_ID) + if (oh->area_id == OSPF_AREA_BACKBONE) zlog_debug ("%s: Message may be via Virtual Link: not supported", __func__); else zlog_debug @@ -1351,19 +1351,6 @@ ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst, assert (p == OSPF6_MESSAGE_END (oh)); - /* RFC2328 Section 10.9: When the neighbor responds to these requests - with the proper Link State Update packet(s), the Link state request - list is truncated and a new Link State Request packet is sent. */ - /* send new Link State Request packet if this LS Update packet - can be recognized as a response to our previous LS Request */ - if (! IN6_IS_ADDR_MULTICAST (dst) && - (on->state == OSPF6_NEIGHBOR_EXCHANGE || - on->state == OSPF6_NEIGHBOR_LOADING)) - { - THREAD_OFF (on->thread_send_lsreq); - on->thread_send_lsreq = - thread_add_event (master, ospf6_lsreq_send, on, 0); - } } static void @@ -1556,7 +1543,7 @@ ospf6_receive (struct thread *thread) } oi = ospf6_interface_lookup_by_ifindex (ifindex); - if (oi == NULL || oi->area == NULL) + if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { zlog_debug ("Message received on disabled interface"); return 0; @@ -1734,6 +1721,13 @@ ospf6_hello_send (struct thread *thread) return 0; } + if (iobuflen == 0) + { + zlog_debug ("Unable to send Hello on interface %s iobuflen is 0", + oi->interface->name); + return 0; + } + /* set next thread */ oi->thread_send_hello = thread_add_timer (master, ospf6_hello_send, oi, oi->hello_interval); @@ -1785,6 +1779,7 @@ ospf6_dbdesc_send (struct thread *thread) struct ospf6_dbdesc *dbdesc; u_char *p; struct ospf6_lsa *lsa; + struct in6_addr *dst; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_dbdesc = (struct thread *) NULL; @@ -1809,7 +1804,8 @@ ospf6_dbdesc_send (struct thread *thread) sizeof (struct ospf6_header)); /* if this is initial one, initialize sequence number for DbDesc */ - if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT)) + if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) && + (on->dbdesc_seqnum == 0)) { struct timeval tv; if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) @@ -1837,7 +1833,7 @@ ospf6_dbdesc_send (struct thread *thread) if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -1848,8 +1844,14 @@ ospf6_dbdesc_send (struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_DBDESC; oh->length = htons (p - sendbuf); - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + dst = &allspfrouters6; + else + dst = &on->linklocal_addr; + + ospf6_send (on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh); + return 0; } @@ -1871,7 +1873,7 @@ ospf6_dbdesc_send_newone (struct thread *thread) { if (size + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -1900,7 +1902,7 @@ ospf6_lsreq_send (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsreq_entry *e; u_char *p; - struct ospf6_lsa *lsa; + struct ospf6_lsa *lsa, *last_req; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_lsreq = (struct thread *) NULL; @@ -1922,13 +1924,9 @@ ospf6_lsreq_send (struct thread *thread) return 0; } - /* set next thread */ - on->thread_send_lsreq = - thread_add_timer (master, ospf6_lsreq_send, on, - on->ospf6_if->rxmt_interval); - memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; + last_req = NULL; /* set Request entries in lsreq */ p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header)); @@ -1938,7 +1936,7 @@ ospf6_lsreq_send (struct thread *thread) /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsreq_entry) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -1947,13 +1945,37 @@ ospf6_lsreq_send (struct thread *thread) e->id = lsa->header->id; e->adv_router = lsa->header->adv_router; p += sizeof (struct ospf6_lsreq_entry); + last_req = lsa; + } + + if (last_req != NULL) + { + if (on->last_ls_req != NULL) + { + ospf6_lsa_unlock (on->last_ls_req); + } + ospf6_lsa_lock (last_req); + on->last_ls_req = last_req; } oh->type = OSPF6_MESSAGE_TYPE_LSREQ; oh->length = htons (p - sendbuf); - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, on->ospf6_if, oh); + else + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + + /* set next thread */ + if (on->request_list->count != 0) + { + on->thread_send_lsreq = + thread_add_timer (master, ospf6_lsreq_send, on, + on->ospf6_if->rxmt_interval); + } + return 0; } @@ -1964,7 +1986,7 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsupdate *lsupdate; u_char *p; - int num; + int lsa_cnt; struct ospf6_lsa *lsa; on = (struct ospf6_neighbor *) THREAD_ARG (thread); @@ -1981,22 +2003,13 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) return 0; } - /* if we have nothing to send, return */ - if (on->lsupdate_list->count == 0 && - on->retrans_list->count == 0) - { - if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) - zlog_debug ("Quit to send (nothing to send)"); - return 0; - } - memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; lsupdate = (struct ospf6_lsupdate *) ((caddr_t) oh + sizeof (struct ospf6_header)); p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); - num = 0; + lsa_cnt = 0; /* lsupdate_list lists those LSA which doesn't need to be retransmitted. remove those from the list */ @@ -2005,58 +2018,85 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) { /* MTU check */ if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) - > ospf6_packet_max(on->ospf6_if)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, on->lsupdate_list); } + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); + lsupdate->lsa_number = htonl (lsa_cnt); + + if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || + (on->ospf6_if->state == OSPF6_INTERFACE_DR) || + (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, + on->ospf6_if, oh); + else + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + } + + /* The addresses used for retransmissions are different from those sent the + first time and so we need to separate them here. + */ + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + lsupdate = (struct ospf6_lsupdate *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); + lsa_cnt = 0; + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; lsa = ospf6_lsdb_next (lsa)) { /* MTU check */ if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) - > ospf6_packet_max(on->ospf6_if)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; } - lsupdate->lsa_number = htonl (num); - - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons (p - sendbuf); - - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); - - if (on->lsupdate_list->count != 0 || - on->retrans_list->count != 0) + if (lsa_cnt) { - if (on->lsupdate_list->count != 0) - on->thread_send_lsupdate = - thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); + lsupdate->lsa_number = htonl (lsa_cnt); + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, + on->ospf6_if, oh); else - on->thread_send_lsupdate = - thread_add_timer (master, ospf6_lsupdate_send_neighbor, on, - on->ospf6_if->rxmt_interval); + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); } + if (on->lsupdate_list->count != 0) + on->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); + else if (on->retrans_list->count != 0) + on->thread_send_lsupdate = + thread_add_timer (master, ospf6_lsupdate_send_neighbor, on, + on->ospf6_if->rxmt_interval); return 0; } @@ -2067,7 +2107,7 @@ ospf6_lsupdate_send_interface (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsupdate *lsupdate; u_char *p; - int num; + int lsa_cnt; struct ospf6_lsa *lsa; oi = (struct ospf6_interface *) THREAD_ARG (thread); @@ -2088,41 +2128,46 @@ ospf6_lsupdate_send_interface (struct thread *thread) memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; lsupdate = (struct ospf6_lsupdate *)((caddr_t) oh + - sizeof (struct ospf6_header)); + sizeof (struct ospf6_header)); p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); - num = 0; + lsa_cnt = 0; for (lsa = ospf6_lsdb_head (oi->lsupdate_list); lsa; lsa = ospf6_lsdb_next (lsa)) { /* MTU check */ if ( (p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE (lsa->header))) - > ospf6_packet_max(oi)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(oi)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, oi->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, oi->lsupdate_list); } - lsupdate->lsa_number = htonl (num); + if (lsa_cnt) + { + lsupdate->lsa_number = htonl (lsa_cnt); - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons (p - sendbuf); + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); - if (oi->state == OSPF6_INTERFACE_DR || - oi->state == OSPF6_INTERFACE_BDR) - ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || + (oi->state == OSPF6_INTERFACE_DR) || + (oi->state == OSPF6_INTERFACE_BDR)) + ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); + else + ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + + } if (oi->lsupdate_list->count > 0) { @@ -2140,6 +2185,7 @@ ospf6_lsack_send_neighbor (struct thread *thread) struct ospf6_header *oh; u_char *p; struct ospf6_lsa *lsa; + int lsa_cnt = 0; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_lsack = (struct thread *) NULL; @@ -2166,16 +2212,16 @@ ospf6_lsack_send_neighbor (struct thread *thread) { /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) - { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF (on->thread_send_lsack); - on->thread_send_lsack = - thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); + { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF (on->thread_send_lsack); + on->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); - ospf6_lsa_unlock (lsa); - break; - } + ospf6_lsdb_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -2183,13 +2229,24 @@ ospf6_lsack_send_neighbor (struct thread *thread) assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, on->lsack_list); + lsa_cnt++; } - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons (p - sendbuf); + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSACK; + oh->length = htons (p - sendbuf); + + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + } + + if (on->thread_send_lsack == NULL && on->lsack_list->count > 0) + { + on->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); + } - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); return 0; } @@ -2200,6 +2257,7 @@ ospf6_lsack_send_interface (struct thread *thread) struct ospf6_header *oh; u_char *p; struct ospf6_lsa *lsa; + int lsa_cnt = 0; oi = (struct ospf6_interface *) THREAD_ARG (thread); oi->thread_send_lsack = (struct thread *) NULL; @@ -2226,16 +2284,16 @@ ospf6_lsack_send_interface (struct thread *thread) { /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(oi)) - { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF (oi->thread_send_lsack); - oi->thread_send_lsack = - thread_add_event (master, ospf6_lsack_send_interface, oi, 0); + { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF (oi->thread_send_lsack); + oi->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_interface, oi, 0); - ospf6_lsa_unlock (lsa); - break; - } + ospf6_lsdb_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, oi->transdelay); memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -2243,16 +2301,21 @@ ospf6_lsack_send_interface (struct thread *thread) assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, oi->lsack_list); + lsa_cnt++; } - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons (p - sendbuf); + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSACK; + oh->length = htons (p - sendbuf); - if (oi->state == OSPF6_INTERFACE_DR || - oi->state == OSPF6_INTERFACE_BDR) - ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || + (oi->state == OSPF6_INTERFACE_DR) || + (oi->state == OSPF6_INTERFACE_BDR)) + ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); + else + ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + } if (oi->thread_send_lsack == NULL && oi->lsack_list->count > 0) { @@ -2263,7 +2326,7 @@ ospf6_lsack_send_interface (struct thread *thread) return 0; } - + /* Commands */ DEFUN (debug_ospf6_message, debug_ospf6_message_cmd, @@ -2337,7 +2400,7 @@ ALIAS (debug_ospf6_message, "Debug only receiving message\n" ) - + DEFUN (no_debug_ospf6_message, no_debug_ospf6_message_cmd, "no debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all)", diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 806767dd..f20c83b9 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -46,6 +46,31 @@ const char *ospf6_neighbor_state_str[] = { "None", "Down", "Attempt", "Init", "Twoway", "ExStart", "ExChange", "Loading", "Full", NULL }; +static const char *ospf6_neighbor_event_str[] = + { + "NoEvent", + "HelloReceived", + "2-WayReceived", + "NegotiationDone", + "ExchangeDone", + "LoadingDone", + "AdjOK?", + "SeqNumberMismatch", + "BadLSReq", + "1-WayReceived", + "InactivityTimer", + }; + +static const char * +ospf6_neighbor_event_string (int event) +{ + #define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent" + + if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT) + return ospf6_neighbor_event_str[event]; + return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; +} + int ospf6_neighbor_cmp (void *va, void *vb) { @@ -98,7 +123,6 @@ ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) on->retrans_list = ospf6_lsdb_create (on); on->dbdesc_list = ospf6_lsdb_create (on); - on->lsreq_list = ospf6_lsdb_create (on); on->lsupdate_list = ospf6_lsdb_create (on); on->lsack_list = ospf6_lsdb_create (on); @@ -121,7 +145,6 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) } ospf6_lsdb_remove_all (on->dbdesc_list); - ospf6_lsdb_remove_all (on->lsreq_list); ospf6_lsdb_remove_all (on->lsupdate_list); ospf6_lsdb_remove_all (on->lsack_list); @@ -130,7 +153,6 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) ospf6_lsdb_delete (on->retrans_list); ospf6_lsdb_delete (on->dbdesc_list); - ospf6_lsdb_delete (on->lsreq_list); ospf6_lsdb_delete (on->lsupdate_list); ospf6_lsdb_delete (on->lsack_list); @@ -145,7 +167,7 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) } static void -ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) +ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on, int event) { u_char prev_state; @@ -161,11 +183,23 @@ ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) /* log */ if (IS_OSPF6_DEBUG_NEIGHBOR (STATE)) { - zlog_debug ("Neighbor state change %s: [%s]->[%s]", on->name, + zlog_debug ("Neighbor state change %s: [%s]->[%s] (%s)", on->name, ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state]); + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); } + /* Optionally notify about adjacency changes */ + if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES) && + (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL) || + (next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state))) + zlog_notice("AdjChg: Nbr %s: %s -> %s (%s)", on->name, + ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); + if (prev_state == OSPF6_NEIGHBOR_FULL || next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_ROUTER_LSA_SCHEDULE (on->ospf6_if->area); @@ -226,7 +260,8 @@ hello_received (struct thread *thread) on->ospf6_if->dead_interval); if (on->state <= OSPF6_NEIGHBOR_DOWN) - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_HELLO_RCVD); return 0; } @@ -249,11 +284,13 @@ twoway_received (struct thread *thread) if (! need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); return 0; } - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -330,7 +367,8 @@ negotiation_done (struct thread *thread) } UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on, + OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE); return 0; } @@ -358,13 +396,45 @@ exchange_done (struct thread *thread) */ if (on->request_list->count == 0) - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); else - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on); + { + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); + + if (on->thread_send_lsreq == NULL) + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + } return 0; } +/* Check loading state. */ +void +ospf6_check_nbr_loading (struct ospf6_neighbor *on) +{ + + /* RFC2328 Section 10.9: When the neighbor responds to these requests + with the proper Link State Update packet(s), the Link state request + list is truncated and a new Link State Request packet is sent. + */ + if ((on->state == OSPF6_NEIGHBOR_LOADING) || + (on->state == OSPF6_NEIGHBOR_EXCHANGE)) + { + if (on->request_list->count == 0) + thread_add_event (master, loading_done, on, 0); + else if (on->last_ls_req == NULL) + { + if (on->thread_send_lsreq != NULL) + THREAD_OFF (on->thread_send_lsreq); + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + } + } +} + int loading_done (struct thread *thread) { @@ -381,7 +451,8 @@ loading_done (struct thread *thread) assert (on->request_list->count == 0); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_LOADING_DONE); return 0; } @@ -400,7 +471,8 @@ adj_ok (struct thread *thread) if (on->state == OSPF6_NEIGHBOR_TWOWAY && need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -413,7 +485,8 @@ adj_ok (struct thread *thread) else if (on->state >= OSPF6_NEIGHBOR_EXSTART && ! need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); ospf6_lsdb_remove_all (on->summary_list); ospf6_lsdb_remove_all (on->request_list); for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; @@ -442,7 +515,8 @@ seqnumber_mismatch (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *SeqNumberMismatch*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -457,6 +531,8 @@ seqnumber_mismatch (struct thread *thread) } THREAD_OFF (on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + on->thread_send_dbdesc = thread_add_event (master, ospf6_dbdesc_send, on, 0); @@ -478,7 +554,8 @@ bad_lsreq (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *BadLSReq*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_BAD_LSREQ); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -493,6 +570,8 @@ bad_lsreq (struct thread *thread) } THREAD_OFF (on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + on->thread_send_dbdesc = thread_add_event (master, ospf6_dbdesc_send, on, 0); @@ -514,7 +593,8 @@ oneway_received (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *1Way-Received*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD); thread_add_event (master, neighbor_change, on->ospf6_if, 0); ospf6_lsdb_remove_all (on->summary_list); @@ -549,7 +629,8 @@ inactivity_timer (struct thread *thread) on->drouter = on->prev_drouter = 0; on->bdrouter = on->prev_bdrouter = 0; - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_DOWN, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_DOWN, on, + OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); thread_add_event (master, neighbor_change, on->ospf6_if, 0); listnode_delete (on->ospf6_if->neighbor_list, on); @@ -559,7 +640,7 @@ inactivity_timer (struct thread *thread) } - + /* vty functions */ /* show neighbor structure */ static void @@ -727,10 +808,10 @@ ospf6_neighbor_show_detail (struct vty *vty, struct ospf6_neighbor *on) timersub (&on->thread_send_lsreq->u.sands, &now, &res); timerstring (&res, duration, sizeof (duration)); vty_out (vty, " %d Pending LSAs for LSReq in Time %s [thread %s]%s", - on->lsreq_list->count, duration, + on->request_list->count, duration, (on->thread_send_lsreq ? "on" : "off"), VNL); - for (lsa = ospf6_lsdb_head (on->lsreq_list); lsa; + for (lsa = ospf6_lsdb_head (on->request_list); lsa; lsa = ospf6_lsdb_next (lsa)) vty_out (vty, " %s%s", lsa->name, VNL); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 5f46c6f3..93ffa289 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -86,6 +86,8 @@ struct ospf6_neighbor struct ospf6_lsdb *lsupdate_list; struct ospf6_lsdb *lsack_list; + struct ospf6_lsa *last_ls_req; + /* Inactivity timer */ struct thread *inactivity_timer; @@ -106,9 +108,22 @@ struct ospf6_neighbor #define OSPF6_NEIGHBOR_LOADING 7 #define OSPF6_NEIGHBOR_FULL 8 +/* Neighbor Events */ +#define OSPF6_NEIGHBOR_EVENT_NO_EVENT 0 +#define OSPF6_NEIGHBOR_EVENT_HELLO_RCVD 1 +#define OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD 2 +#define OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE 3 +#define OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE 4 +#define OSPF6_NEIGHBOR_EVENT_LOADING_DONE 5 +#define OSPF6_NEIGHBOR_EVENT_ADJ_OK 6 +#define OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH 7 +#define OSPF6_NEIGHBOR_EVENT_BAD_LSREQ 8 +#define OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD 9 +#define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER 10 +#define OSPF6_NEIGHBOR_EVENT_MAX_EVENT 11 + extern const char *ospf6_neighbor_state_str[]; - /* Function Prototypes */ int ospf6_neighbor_cmp (void *va, void *vb); void ospf6_neighbor_dbex_init (struct ospf6_neighbor *on); @@ -130,6 +145,7 @@ extern int seqnumber_mismatch (struct thread *); extern int bad_lsreq (struct thread *); extern int oneway_received (struct thread *); extern int inactivity_timer (struct thread *); +extern void ospf6_check_nbr_loading (struct ospf6_neighbor *); extern void ospf6_neighbor_init (void); extern int config_write_ospf6_debug_neighbor (struct vty *vty); diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index e5a1436c..e0be38b3 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -27,6 +27,7 @@ #include "sockopt.h" #include "privs.h" +#include "libospf.h" #include "ospf6_proto.h" #include "ospf6_network.h" @@ -36,18 +37,8 @@ int ospf6_sock; struct in6_addr allspfrouters6; struct in6_addr alldrouters6; -/* setsockopt ReUseAddr to on */ -void -ospf6_set_reuseaddr (void) -{ - u_int on = 0; - if (setsockopt (ospf6_sock, SOL_SOCKET, SO_REUSEADDR, &on, - sizeof (u_int)) < 0) - zlog_warn ("Network: set SO_REUSEADDR failed: %s", safe_strerror (errno)); -} - /* setsockopt MulticastLoop to off */ -void +static void ospf6_reset_mcastloop (void) { u_int off = 0; @@ -57,13 +48,13 @@ ospf6_reset_mcastloop (void) safe_strerror (errno)); } -void +static void ospf6_set_pktinfo (void) { setsockopt_ipv6_pktinfo (ospf6_sock, 1); } -void +static void ospf6_set_transport_class (void) { #ifdef IPTOS_PREC_INTERNETCONTROL @@ -71,7 +62,7 @@ ospf6_set_transport_class (void) #endif } -void +static void ospf6_set_checksum (void) { int offset = 12; @@ -205,7 +196,7 @@ ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst, smsghdr.msg_name = (caddr_t) &dst_sin6; smsghdr.msg_namelen = sizeof (struct sockaddr_in6); smsghdr.msg_control = (caddr_t) cmsgbuf; - smsghdr.msg_controllen = sizeof (cmsgbuf); + smsghdr.msg_controllen = scmsgp->cmsg_len; retval = sendmsg (ospf6_sock, &smsghdr, 0); if (retval != iov_totallen (message)) diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 0526b3e1..127bf45c 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -22,18 +22,12 @@ #ifndef OSPF6_NETWORK_H #define OSPF6_NETWORK_H - + extern int ospf6_sock; extern struct in6_addr allspfrouters6; extern struct in6_addr alldrouters6; -/* Function Prototypes */ -extern void ospf6_set_reuseaddr (void); -extern void ospf6_reset_mcastloop (void); -extern void ospf6_set_pktinfo (void); -extern void ospf6_set_checksum (void); - extern int ospf6_serv_sock (void); extern void ospf6_sso (u_int ifindex, struct in6_addr *group, int option); diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index 64625004..af60eb92 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -26,33 +26,12 @@ /* OSPF protocol version */ #define OSPFV3_VERSION 3 -/* OSPF protocol number. */ -#ifndef IPPROTO_OSPFIGP -#define IPPROTO_OSPFIGP 89 -#endif - /* TOS field normaly null */ #define DEFAULT_TOS_VALUE 0x0 -/* Architectural Constants */ -#define LS_REFRESH_TIME 1800 /* 30 min */ -#define MIN_LS_INTERVAL 5 -#define MIN_LS_ARRIVAL 1 -#define MAXAGE 3600 /* 1 hour */ -#define CHECK_AGE 300 /* 5 min */ -#define MAX_AGE_DIFF 900 /* 15 min */ -#define LS_INFINITY 0xffffff /* 24-bit binary value */ -#define INITIAL_SEQUENCE_NUMBER 0x80000001 /* signed 32-bit integer */ -#define MAX_SEQUENCE_NUMBER 0x7fffffff /* signed 32-bit integer */ - #define ALLSPFROUTERS6 "ff02::5" #define ALLDROUTERS6 "ff02::6" -/* Configurable Constants */ - -#define DEFAULT_HELLO_INTERVAL 10 -#define DEFAULT_ROUTER_DEAD_INTERVAL 40 - #define OSPF6_ROUTER_BIT_W (1 << 3) #define OSPF6_ROUTER_BIT_V (1 << 2) #define OSPF6_ROUTER_BIT_E (1 << 1) diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 398acfa8..9e6b33e5 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -304,7 +304,7 @@ ospf6_route_lookup_bestmatch (struct prefix *prefix, return route; } -#ifndef NDEBUG +#ifdef DEBUG static void route_table_assert (struct ospf6_route_table *table) { @@ -350,7 +350,7 @@ route_table_assert (struct ospf6_route_table *table) #define ospf6_route_table_assert(t) (route_table_assert (t)) #else #define ospf6_route_table_assert(t) ((void) 0) -#endif /*NDEBUG*/ +#endif /*DEBUG*/ struct ospf6_route * ospf6_route_add (struct ospf6_route *route, @@ -780,7 +780,7 @@ ospf6_route_table_delete (struct ospf6_route_table *table) XFREE (MTYPE_OSPF6_ROUTE, table); } - + /* VTY commands */ void ospf6_route_show (struct vty *vty, struct ospf6_route *route) diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index b384824c..c0dcf9f1 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -122,6 +122,10 @@ struct ospf6_route /* Destination Type */ u_char type; + /* XXX: It would likely be better to use separate struct in_addr's + * for the advertising router-ID and prefix IDs, instead of stuffing them + * into one. See also XXX below. + */ /* Destination ID */ struct prefix prefix; @@ -235,6 +239,7 @@ extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; sizeof (struct ospf6_nexthop) * OSPF6_MULTI_PATH_LIMIT) == 0) #define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST)) +/* XXX: This gives GCC heartburn aboutbreaking aliasing rules. */ #define ospf6_linkstate_prefix_adv_router(x) \ (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0])) #define ospf6_linkstate_prefix_id(x) \ diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index f8a3b920..4be8be04 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -488,7 +488,9 @@ ospfv3GeneralGroup (struct variable *v, oid *name, size_t *length, case OSPFv3DEMANDEXTENSIONS: return SNMP_INTEGER (0); /* Not supported */ case OSPFv3REFERENCEBANDWIDTH: - return SNMP_INTEGER (100000); + if (ospf6) + return SNMP_INTEGER (ospf6->ref_bandwidth); + /* Otherwise, like for "not implemented". */ case OSPFv3RESTARTSUPPORT: case OSPFv3RESTARTINTERVAL: case OSPFv3RESTARTSTRICTLSACHECKING: @@ -528,7 +530,7 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length, return NULL; len = *length - v->namelen; - len = (len >= 1 ? sizeof 1 : 0); + len = (len >= 1 ? 1 : 0); if (exact && len != 1) return NULL; if (len) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index da0ee131..d0e9101b 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -40,6 +40,7 @@ #include "ospf6_intra.h" #include "ospf6_interface.h" #include "ospf6d.h" +#include "ospf6_abr.h" unsigned char conf_debug_ospf6_spf = 0; @@ -377,6 +378,36 @@ ospf6_spf_table_finish (struct ospf6_route_table *result_table) } } +static const char *ospf6_spf_reason_str[] = + { + "R+", + "R-", + "N+", + "N-", + "L+", + "L-", + "R*", + "N*", + }; + +void ospf6_spf_reason_string (unsigned int reason, char *buf, int size) +{ + size_t bit; + int len = 0; + + if (!buf) + return; + + for (bit = 0; bit <= (sizeof(ospf6_spf_reason_str) / sizeof(char *)); bit++) + { + if ((reason & (1 << bit)) && (len < size)) + { + len += snprintf((buf + len), (size - len), "%s%s", + (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]); + } + } +} + /* RFC2328 16.1. Calculating the shortest-path tree for an area */ /* RFC2740 3.8.1. Calculating the shortest path tree for an area */ void @@ -424,6 +455,11 @@ ospf6_spf_calculation (u_int32_t router_id, if (ospf6_spf_install (v, result_table) < 0) continue; + /* Skip overloaded routers */ + if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) && + ospf6_router_is_stub_router (v->lsa))) + continue; + /* For each LS description in the just-added vertex V's LSA */ size = (VERTEX_IS_TYPE (ROUTER, v) ? sizeof (struct ospf6_router_lsdesc) : @@ -506,39 +542,146 @@ static int ospf6_spf_calculation_thread (struct thread *t) { struct ospf6_area *oa; + struct ospf6 *ospf6; struct timeval start, end, runtime; + struct listnode *node; + struct ospf6_route *route; + int areas_processed = 0; + char rbuf[32]; - oa = (struct ospf6_area *) THREAD_ARG (t); - oa->thread_spf_calculation = NULL; - - if (IS_OSPF6_DEBUG_SPF (PROCESS)) - zlog_debug ("SPF calculation for Area %s", oa->name); - if (IS_OSPF6_DEBUG_SPF (DATABASE)) - ospf6_spf_log_database (oa); + ospf6 = (struct ospf6 *)THREAD_ARG (t); + ospf6->t_spf_calc = NULL; /* execute SPF calculation */ quagga_gettime (QUAGGA_CLK_MONOTONIC, &start); - ospf6_spf_calculation (oa->ospf6->router_id, oa->spf_table, oa); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) + { + + if (oa == ospf6->backbone) + continue; + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Area %s", oa->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database (oa); + + ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa); + ospf6_intra_route_calculation (oa); + ospf6_intra_brouter_calculation (oa); + + areas_processed++; + } + + if (ospf6->backbone) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Backbone area %s", + ospf6->backbone->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database(ospf6->backbone); + + ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table, + ospf6->backbone); + ospf6_intra_route_calculation(ospf6->backbone); + ospf6_intra_brouter_calculation(ospf6->backbone); + areas_processed++; + } + + /* Redo summaries if required */ + for (route = ospf6_route_head (ospf6->route_table); route; + route = ospf6_route_next (route)) + ospf6_abr_originate_summary(route); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &end); timersub (&end, &start, &runtime); + ospf6->ts_spf_duration = runtime; + + ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf)); + if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) zlog_debug ("SPF runtime: %ld sec %ld usec", runtime.tv_sec, runtime.tv_usec); - ospf6_intra_route_calculation (oa); - ospf6_intra_brouter_calculation (oa); - + zlog_info("SPF processing: # Areas: %d, SPF runtime: %ld sec %ld usec, " + "Reason: %s\n", areas_processed, runtime.tv_sec, runtime.tv_usec, + rbuf); + ospf6->last_spf_reason = ospf6->spf_reason; + ospf6_reset_spf_reason(ospf6); return 0; } +/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we + set timer for SPF calc. */ void -ospf6_spf_schedule (struct ospf6_area *oa) +ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason) { - if (oa->thread_spf_calculation) + unsigned long delay, elapsed, ht; + struct timeval now, result; + + ospf6_set_spf_reason(ospf6, reason); + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + { + char rbuf[32]; + ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf)); + zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf); + } + + /* OSPF instance does not exist. */ + if (ospf6 == NULL) return; - oa->thread_spf_calculation = - thread_add_event (master, ospf6_spf_calculation_thread, oa, 0); + + /* SPF calculation timer is already scheduled. */ + if (ospf6->t_spf_calc) + { + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF: calculation timer is already scheduled: %p", + ospf6->t_spf_calc); + return; + } + + /* XXX Monotic timers: we only care about relative time here. */ + now = recent_relative_time (); + timersub (&now, &ospf6->ts_spf, &result); + + elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); + ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier; + + if (ht > ospf6->spf_max_holdtime) + ht = ospf6->spf_max_holdtime; + + /* Get SPF calculation delay time. */ + if (elapsed < ht) + { + /* Got an event within the hold time of last SPF. We need to + * increase the hold_multiplier, if it's not already at/past + * maximum value, and wasn't already increased.. + */ + if (ht < ospf6->spf_max_holdtime) + ospf6->spf_hold_multiplier++; + + /* always honour the SPF initial delay */ + if ( (ht - elapsed) < ospf6->spf_delay) + delay = ospf6->spf_delay; + else + delay = ht - elapsed; + } + else + { + /* Event is past required hold-time of last SPF */ + delay = ospf6->spf_delay; + ospf6->spf_hold_multiplier = 1; + } + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF: calculation timer delay = %ld", delay); + + zlog_info ("SPF: Scheduled in %ld msec", delay); + + ospf6->t_spf_calc = + thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay); } void @@ -666,6 +809,59 @@ DEFUN (no_debug_ospf6_spf_database, return CMD_SUCCESS; } +static int +ospf6_timers_spf_set (struct vty *vty, unsigned int delay, + unsigned int hold, + unsigned int max) +{ + struct ospf6 *ospf = vty->index; + + ospf->spf_delay = delay; + ospf->spf_holdtime = hold; + ospf->spf_max_holdtime = max; + + return CMD_SUCCESS; +} + +DEFUN (ospf6_timers_throttle_spf, + ospf6_timers_throttle_spf_cmd, + "timers throttle spf <0-600000> <0-600000> <0-600000>", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + unsigned int delay, hold, max; + + if (argc != 3) + { + vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000); + VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000); + VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000); + + return ospf6_timers_spf_set (vty, delay, hold, max); +} + +DEFUN (no_ospf6_timers_throttle_spf, + no_ospf6_timers_throttle_spf_cmd, + "no timers throttle spf", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n") +{ + return ospf6_timers_spf_set (vty, + OSPF_SPF_DELAY_DEFAULT, + OSPF_SPF_HOLDTIME_DEFAULT, + OSPF_SPF_MAX_HOLDTIME_DEFAULT); +} + int config_write_ospf6_debug_spf (struct vty *vty) { @@ -678,6 +874,19 @@ config_write_ospf6_debug_spf (struct vty *vty) return 0; } +void +ospf6_spf_config_write (struct vty *vty) +{ + + if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT || + ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT || + ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) + vty_out (vty, " timers throttle spf %d %d %d%s", + ospf6->spf_delay, ospf6->spf_holdtime, + ospf6->spf_max_holdtime, VTY_NEWLINE); + +} + void install_element_ospf6_debug_spf (void) { @@ -698,6 +907,6 @@ install_element_ospf6_debug_spf (void) void ospf6_spf_init (void) { + install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd); + install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd); } - - diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index c7069c25..b3481dc3 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -22,6 +22,8 @@ #ifndef OSPF6_SPF_H #define OSPF6_SPF_H +#include "ospf6_top.h" + /* Debug option */ extern unsigned char conf_debug_ospf6_spf; #define OSPF6_DEBUG_SPF_PROCESS 0x01 @@ -77,18 +79,86 @@ struct ospf6_vertex #define VERTEX_IS_TYPE(t, v) \ ((v)->type == OSPF6_VERTEX_TYPE_ ## t ? 1 : 0) +/* What triggered the SPF? */ +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED (1 << 0) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED (1 << 1) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED (1 << 2) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED (1 << 3) +#define OSPF6_SPF_FLAGS_LINK_LSA_ADDED (1 << 4) +#define OSPF6_SPF_FLAGS_LINK_LSA_REMOVED (1 << 5) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED (1 << 6) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) + +static inline void +ospf6_set_spf_reason (struct ospf6* ospf, unsigned int reason) +{ + ospf->spf_reason |= reason; +} + +static inline void +ospf6_reset_spf_reason (struct ospf6 *ospf) +{ + ospf->spf_reason = 0; +} + +static inline unsigned int +ospf6_lsadd_to_spf_reason (struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_ADDED; + break; + default: + break; + } + return (reason); +} + +static inline unsigned int +ospf6_lsremove_to_spf_reason (struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_REMOVED; + break; + default: + break; + } + return (reason); +} + extern void ospf6_spf_table_finish (struct ospf6_route_table *result_table); extern void ospf6_spf_calculation (u_int32_t router_id, struct ospf6_route_table *result_table, struct ospf6_area *oa); -extern void ospf6_spf_schedule (struct ospf6_area *oa); +extern void ospf6_spf_schedule (struct ospf6 *ospf, unsigned int reason); extern void ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest, struct ospf6_vertex *v); +extern void ospf6_spf_config_write (struct vty *vty); extern int config_write_ospf6_debug_spf (struct vty *vty); extern void install_element_ospf6_debug_spf (void); extern void ospf6_spf_init (void); +extern void ospf6_spf_reason_string (unsigned int reason, char *buf, int size); #endif /* OSPF6_SPF_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index df856b4c..e4e6f17a 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -46,6 +46,7 @@ #include "ospf6_asbr.h" #include "ospf6_abr.h" #include "ospf6_intra.h" +#include "ospf6_spf.h" #include "ospf6d.h" /* global ospf6d variable */ @@ -127,6 +128,11 @@ ospf6_create (void) o->lsdb->hook_add = ospf6_top_lsdb_hook_add; o->lsdb->hook_remove = ospf6_top_lsdb_hook_remove; + o->spf_delay = OSPF_SPF_DELAY_DEFAULT; + o->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT; + o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; + o->spf_hold_multiplier = 1; + o->route_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, ROUTES); o->route_table->scope = o; o->route_table->hook_add = ospf6_top_route_hook_add; @@ -142,6 +148,8 @@ ospf6_create (void) o->external_id_table = route_table_init (); + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + return o; } @@ -155,6 +163,8 @@ ospf6_delete (struct ospf6 *o) for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) ospf6_area_delete (oa); + + list_delete (o->area_list); ospf6_lsdb_delete (o->lsdb); @@ -170,6 +180,7 @@ ospf6_delete (struct ospf6 *o) } static void +__attribute__((unused)) ospf6_enable (struct ospf6 *o) { struct listnode *node, *nnode; @@ -196,9 +207,16 @@ ospf6_disable (struct ospf6 *o) for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) ospf6_area_disable (oa); + /* XXX: This also changes persistent settings */ + ospf6_asbr_redistribute_reset(); + ospf6_lsdb_remove_all (o->lsdb); ospf6_route_remove_all (o->route_table); ospf6_route_remove_all (o->brouter_table); + + THREAD_OFF(o->maxage_remover); + THREAD_OFF(o->t_spf_calc); + THREAD_OFF(o->t_ase_calc); } } @@ -210,6 +228,7 @@ ospf6_maxage_remover (struct thread *thread) struct ospf6_interface *oi; struct ospf6_neighbor *on; struct listnode *i, *j, *k; + int reschedule = 0; o->maxage_remover = (struct thread *) NULL; @@ -221,8 +240,9 @@ ospf6_maxage_remover (struct thread *thread) { if (on->state != OSPF6_NEIGHBOR_EXCHANGE && on->state != OSPF6_NEIGHBOR_LOADING) - continue; + continue; + ospf6_maxage_remove (o); return 0; } } @@ -231,11 +251,28 @@ ospf6_maxage_remover (struct thread *thread) for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) { for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) - OSPF6_LSDB_MAXAGE_REMOVER (oi->lsdb); + { + if (ospf6_lsdb_maxage_remover (oi->lsdb)) + { + reschedule = 1; + } + } - OSPF6_LSDB_MAXAGE_REMOVER (oa->lsdb); + if (ospf6_lsdb_maxage_remover (oa->lsdb)) + { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover (o->lsdb)) + { + reschedule = 1; + } + + if (reschedule) + { + ospf6_maxage_remove (o); } - OSPF6_LSDB_MAXAGE_REMOVER (o->lsdb); return 0; } @@ -244,7 +281,8 @@ void ospf6_maxage_remove (struct ospf6 *o) { if (o && ! o->maxage_remover) - o->maxage_remover = thread_add_event (master, ospf6_maxage_remover, o, 0); + o->maxage_remover = thread_add_timer (master, ospf6_maxage_remover, o, + OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT); } /* start ospf6 */ @@ -256,8 +294,6 @@ DEFUN (router_ospf6, { if (ospf6 == NULL) ospf6 = ospf6_create (); - if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - ospf6_enable (ospf6); /* set current ospf point. */ vty->node = OSPF6_NODE; @@ -273,10 +309,13 @@ DEFUN (no_router_ospf6, NO_STR OSPF6_ROUTER_STR) { - if (ospf6 == NULL || CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - vty_out (vty, "OSPFv3 is not running%s", VNL); + if (ospf6 == NULL) + vty_out (vty, "OSPFv3 is not configured%s", VNL); else - ospf6_disable (ospf6); + { + ospf6_delete (ospf6); + ospf6 = NULL; + } /* return to config node . */ vty->node = CONFIG_NODE; @@ -312,6 +351,56 @@ DEFUN (ospf6_router_id, return CMD_SUCCESS; } +DEFUN (ospf6_log_adjacency_changes, + ospf6_log_adjacency_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") +{ + struct ospf6 *ospf6 = vty->index; + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (ospf6_log_adjacency_changes_detail, + ospf6_log_adjacency_changes_detail_cmd, + "log-adjacency-changes detail", + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf6 *ospf6 = vty->index; + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes, + no_ospf6_log_adjacency_changes_cmd, + "no log-adjacency-changes", + NO_STR + "Log changes in adjacency state\n") +{ + struct ospf6 *ospf6 = vty->index; + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes_detail, + no_ospf6_log_adjacency_changes_detail_cmd, + "no log-adjacency-changes detail", + NO_STR + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf6 *ospf6 = vty->index; + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + DEFUN (ospf6_interface_area, ospf6_interface_area_cmd, "interface IFNAME area A.B.C.D", @@ -359,8 +448,12 @@ DEFUN (ospf6_interface_area, SET_FLAG (oa->flag, OSPF6_AREA_ENABLE); + /* ospf6 process is currently disabled, not much more to do */ + if (CHECK_FLAG (o->flag, OSPF6_DISABLED)) + return CMD_SUCCESS; + /* start up */ - thread_add_event (master, interface_up, oi, 0); + ospf6_interface_enable (oi); /* If the router is ABR, originate summary routes */ if (ospf6_is_router_abr (o)) @@ -379,14 +472,11 @@ DEFUN (no_ospf6_interface_area, "OSPF6 area ID in IPv4 address notation\n" ) { - struct ospf6 *o; struct ospf6_interface *oi; struct ospf6_area *oa; struct interface *ifp; u_int32_t area_id; - o = (struct ospf6 *) vty->index; - ifp = if_lookup_by_name (argv[0]); if (ifp == NULL) { @@ -438,13 +528,109 @@ DEFUN (no_ospf6_interface_area, return CMD_SUCCESS; } +DEFUN (ospf6_stub_router_admin, + ospf6_stub_router_admin_cmd, + "stub-router administrative", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + if (!CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE (oa); + } + SET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_admin, + no_ospf6_stub_router_admin_cmd, + "no stub-router administrative", + NO_STR + "Make router a stub router\n" + "Advertise ability to be a transit router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE (oa); + } + UNSET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf6_stub_router_startup, + ospf6_stub_router_startup_cmd, + "stub-router on-startup <5-86400>", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router on startup of OSPF6\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_startup, + no_ospf6_stub_router_startup_cmd, + "no stub-router on-startup", + NO_STR + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router on startup of OSPF6\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (ospf6_stub_router_shutdown, + ospf6_stub_router_shutdown_cmd, + "stub-router on-shutdown <5-86400>", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router before shutdown\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_shutdown, + no_ospf6_stub_router_shutdown_cmd, + "no stub-router on-shutdown", + NO_STR + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router before shutdown\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + static void ospf6_show (struct vty *vty, struct ospf6 *o) { struct listnode *n; struct ospf6_area *oa; char router_id[16], duration[32]; - struct timeval now, running; + struct timeval now, running, result; + char buf[32], rbuf[32]; /* process id, router id */ inet_ntop (AF_INET, &o->router_id, router_id, sizeof (router_id)); @@ -460,6 +646,35 @@ ospf6_show (struct vty *vty, struct ospf6 *o) /* Redistribute configuration */ /* XXX */ + /* Show SPF parameters */ + vty_out(vty, " Initial SPF scheduling delay %d millisec(s)%s" + " Minimum hold time between consecutive SPFs %d millsecond(s)%s" + " Maximum hold time between consecutive SPFs %d millsecond(s)%s" + " Hold time multiplier is currently %d%s", + o->spf_delay, VNL, + o->spf_holdtime, VNL, + o->spf_max_holdtime, VNL, + o->spf_hold_multiplier, VNL); + + vty_out(vty, " SPF algorithm "); + if (o->ts_spf.tv_sec || o->ts_spf.tv_usec) + { + timersub(&now, &o->ts_spf, &result); + timerstring(&result, buf, sizeof(buf)); + ospf6_spf_reason_string(o->last_spf_reason, rbuf, sizeof(rbuf)); + vty_out(vty, "last executed %s ago, reason %s%s", buf, rbuf, VNL); + vty_out (vty, " Last SPF duration %ld sec %ld usec%s", + o->ts_spf_duration.tv_sec, o->ts_spf_duration.tv_usec, VNL); + } + else + vty_out(vty, "has not been run$%s", VNL); + threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf)); + vty_out (vty, " SPF timer %s%s%s", + (o->t_spf_calc ? "due in " : "is "), buf, VNL); + + if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER)) + vty_out (vty, " Router Is Stub Router%s", VNL); + /* LSAs */ vty_out (vty, " Number of AS scoped LSAs is %u%s", o->lsdb->count, VNL); @@ -468,6 +683,16 @@ ospf6_show (struct vty *vty, struct ospf6 *o) vty_out (vty, " Number of areas in this router is %u%s", listcount (o->area_list), VNL); + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) + { + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " All adjacency changes are logged%s",VTY_NEWLINE); + else + vty_out(vty, " Adjacency changes are logged%s",VTY_NEWLINE); + } + + vty_out (vty, "%s",VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (o->area_list, n, oa)) ospf6_area_show (vty, oa); } @@ -628,6 +853,16 @@ DEFUN (show_ipv6_ospf6_route_type_detail, return CMD_SUCCESS; } +static void +ospf6_stub_router_config_write (struct vty *vty) +{ + if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + vty_out (vty, " stub-router administrative%s", VNL); + } + return; +} + /* OSPF configuration write function. */ static int config_write_ospf6 (struct vty *vty) @@ -640,16 +875,29 @@ config_write_ospf6 (struct vty *vty) /* OSPFv6 configuration. */ if (ospf6 == NULL) return CMD_SUCCESS; - if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - return CMD_SUCCESS; inet_ntop (AF_INET, &ospf6->router_id_static, router_id, sizeof (router_id)); vty_out (vty, "router ospf6%s", VNL); if (ospf6->router_id_static != 0) vty_out (vty, " router-id %s%s", router_id, VNL); + /* log-adjacency-changes flag print. */ + if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) + { + vty_out(vty, " log-adjacency-changes"); + if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " detail"); + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (ospf6->ref_bandwidth != OSPF6_REFERENCE_BANDWIDTH) + vty_out (vty, " auto-cost reference-bandwidth %d%s", ospf6->ref_bandwidth / 1000, + VNL); + + ospf6_stub_router_config_write (vty); ospf6_redistribute_config_write (vty); ospf6_area_config_write (vty); + ospf6_spf_config_write (vty); for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, j, oa)) { @@ -700,8 +948,20 @@ ospf6_top_init (void) install_default (OSPF6_NODE); install_element (OSPF6_NODE, &ospf6_router_id_cmd); + install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_cmd); + install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd); + install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd); + install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_detail_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_cmd); install_element (OSPF6_NODE, &no_ospf6_interface_area_cmd); + install_element (OSPF6_NODE, &ospf6_stub_router_admin_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_admin_cmd); + /* For a later time + install_element (OSPF6_NODE, &ospf6_stub_router_startup_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_startup_cmd); + install_element (OSPF6_NODE, &ospf6_stub_router_shutdown_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_shutdown_cmd); + */ } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 4b2d2c3e..d6f4bf0f 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -38,6 +38,7 @@ struct ospf6 /* list of areas */ struct list *area_list; + struct ospf6_area *backbone; /* AS scope link state database */ struct ospf6_lsdb *lsdb; @@ -59,10 +60,32 @@ struct ospf6 u_char flag; + /* Configured flags */ + u_char config_flags; +#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0) +#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1) + + /* SPF parameters */ + unsigned int spf_delay; /* SPF delay time. */ + unsigned int spf_holdtime; /* SPF hold time. */ + unsigned int spf_max_holdtime; /* SPF maximum-holdtime */ + unsigned int spf_hold_multiplier; /* Adaptive multiplier for hold time */ + unsigned int spf_reason; /* reason bits while scheduling SPF */ + + struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ + unsigned int last_spf_reason; /* Last SPF reason */ + + /* Threads */ + struct thread *t_spf_calc; /* SPF calculation timer. */ + struct thread *t_ase_calc; /* ASE calculation timer. */ struct thread *maxage_remover; + + u_int32_t ref_bandwidth; }; #define OSPF6_DISABLED 0x01 +#define OSPF6_STUB_ROUTER 0x02 /* global pointer for OSPF top data structure */ extern struct ospf6 *ospf6; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index f09e9d22..85f70647 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -117,7 +117,9 @@ ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) ifp->name, ifp->ifindex, ifp->mtu6); #if 0 - /* Why is this commented out? */ + /* XXX: ospf6_interface_if_del is not the right way to handle this, + * because among other thinkable issues, it will also clear all + * settings as they are contained in the struct ospf6_interface. */ ospf6_interface_if_del (ifp); #endif /*0*/ @@ -137,9 +139,9 @@ ospf6_zebra_if_state_update (int command, struct zclient *zclient, if (IS_OSPF6_DEBUG_ZEBRA (RECV)) zlog_debug ("Zebra Interface state change: " - "%s index %d flags %llx metric %d mtu %d", + "%s index %d flags %llx metric %d mtu %d bandwidth %d", ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu6); + ifp->metric, ifp->mtu6, ifp->bandwidth); ospf6_interface_state_update (ifp); return 0; @@ -163,8 +165,10 @@ ospf6_zebra_if_address_update_add (int command, struct zclient *zclient, buf, sizeof (buf)), c->address->prefixlen); if (c->address->family == AF_INET6) - ospf6_interface_connected_route_update (c->ifp); - + { + ospf6_interface_state_update (c->ifp); + ospf6_interface_connected_route_update (c->ifp); + } return 0; } @@ -186,7 +190,10 @@ ospf6_zebra_if_address_update_delete (int command, struct zclient *zclient, buf, sizeof (buf)), c->address->prefixlen); if (c->address->family == AF_INET6) - ospf6_interface_connected_route_update (c->ifp); + { + ospf6_interface_connected_route_update (c->ifp); + ospf6_interface_state_update (c->ifp); + } return 0; } @@ -267,7 +274,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, - + DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -595,7 +602,7 @@ ospf6_zebra_init (void) } /* Debug */ - + DEFUN (debug_ospf6_zebra_sendrecv, debug_ospf6_zebra_sendrecv_cmd, "debug ospf6 zebra (send|recv)", diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 3fdbda18..3cdd5c11 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -77,7 +77,7 @@ route_prev (struct route_node *node) return prev; } - + /* show database functions */ DEFUN (show_version_ospf6, show_version_ospf6_cmd, diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 2ac6300e..4122b309 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -24,15 +24,12 @@ #define OSPF6_DAEMON_VERSION "0.9.7r" +#include "libospf.h" +#include "thread.h" + /* global variables */ extern struct thread_master *master; -#ifdef INRIA_IPV6 -#ifndef IPV6_PKTINFO -#define IPV6_PKTINFO IPV6_RECVPKTINFO -#endif /* IPV6_PKTINFO */ -#endif /* INRIA_IPV6 */ - /* Historical for KAME. */ #ifndef IPV6_JOIN_GROUP #ifdef IPV6_ADD_MEMBERSHIP @@ -98,6 +95,17 @@ extern struct thread_master *master; zlog_warn ("strftime error"); \ } while (0) +#define threadtimer_string(now, t, buf, size) \ + do { \ + struct timeval result; \ + if (!t) \ + snprintf(buf, size, "inactive"); \ + else { \ + timersub(&t->u.sands, &now, &result); \ + timerstring(&result, buf, size); \ + } \ +} while (0) + /* for commands */ #define OSPF6_AREA_STR "Area information\n" #define OSPF6_AREA_ID_STR "Area ID (as an IPv4 notation)\n" @@ -113,7 +121,7 @@ extern struct thread_master *master; return CMD_SUCCESS; \ } - + /* Function Prototypes */ extern struct route_node *route_prev (struct route_node *node); diff --git a/ospfclient/COPYING b/ospfclient/COPYING index a43ea212..b8cf3a1a 100644 --- a/ospfclient/COPYING +++ b/ospfclient/COPYING @@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest diff --git a/ospfclient/Makefile.am b/ospfclient/Makefile.am index 0f37aa98..09d2ba86 100644 --- a/ospfclient/Makefile.am +++ b/ospfclient/Makefile.am @@ -1,6 +1,6 @@ ## Automake.am for OSPF API client -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib lib_LTLIBRARIES = libospfapiclient.la libospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index 3608ebac..1de7644f 100644 --- a/ospfclient/ospfclient.c +++ b/ospfclient/ospfclient.c @@ -1,3 +1,21 @@ +/* This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + /* * Simple program to demonstrate how OSPF API can be used. This * application retrieves the LSDB from the OSPF daemon and then diff --git a/ospfd/Makefile.am b/ospfd/Makefile.am index 4e1a4fe9..4160bfcc 100644 --- a/ospfd/Makefile.am +++ b/ospfd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f5edc99e..e172e53c 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_dump.h" - + static struct ospf_area_range * ospf_area_range_new (struct prefix_ipv4 *p) { @@ -556,7 +556,7 @@ ospf_check_abr_status (struct ospf *ospf) if (new_flags != ospf->flags) { - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ABR_STATUS_CHANGE); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_check_abr_status(): new router flags: %x",new_flags); ospf->flags = new_flags; @@ -1140,7 +1140,8 @@ ospf_abr_announce_rtr_to_area (struct prefix_ipv4 *p, u_int32_t cost, GET_METRIC (slsa->metric), cost); } - if (old && (GET_METRIC (slsa->metric) == cost)) + if (old && (GET_METRIC (slsa->metric) == cost) && + ((old->flags & OSPF_LSA_IN_MAXAGE) == 0)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_announce_rtr_to_area(): old summary approved"); diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 74a49e3b..cbb234ae 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -464,7 +464,7 @@ new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter) { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_register_event *emsg; - int len; + size_t len; emsg = (struct msg_register_event *) buf; len = sizeof (struct msg_register_event) + @@ -472,6 +472,9 @@ new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter) emsg->filter.typemask = htons (filter->typemask); emsg->filter.origin = filter->origin; emsg->filter.num_areas = filter->num_areas; + if (len > sizeof (buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ return msg_new (MSG_REGISTER_EVENT, emsg, seqnum, len); } @@ -480,7 +483,7 @@ new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter) { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_sync_lsdb *smsg; - int len; + size_t len; smsg = (struct msg_sync_lsdb *) buf; len = sizeof (struct msg_sync_lsdb) + @@ -488,6 +491,9 @@ new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter) smsg->filter.typemask = htons (filter->typemask); smsg->filter.origin = filter->origin; smsg->filter.num_areas = filter->num_areas; + if (len > sizeof (buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ return msg_new (MSG_SYNC_LSDB, smsg, seqnum, len); } @@ -498,16 +504,18 @@ new_msg_originate_request (u_int32_t seqnum, struct in_addr area_id, struct lsa_header *data) { struct msg_originate_request *omsg; - int omsglen; + size_t omsglen; char buf[OSPF_API_MAX_MSG_SIZE]; - omsglen = sizeof (struct msg_originate_request) - sizeof (struct lsa_header) - + ntohs (data->length); - omsg = (struct msg_originate_request *) buf; omsg->ifaddr = ifaddr; omsg->area_id = area_id; - memcpy (&omsg->data, data, ntohs (data->length)); + + omsglen = ntohs (data->length); + if (omsglen > sizeof (buf) - offsetof (struct msg_originate_request, data)) + omsglen = sizeof (buf) - offsetof (struct msg_originate_request, data); + memcpy (&omsg->data, data, omsglen); + omsglen += sizeof (struct msg_originate_request) - sizeof (struct lsa_header); return msg_new (MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen); } @@ -622,18 +630,21 @@ new_msg_lsa_change_notify (u_char msgtype, { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_lsa_change_notify *nmsg; - int len; + size_t len; assert (data); nmsg = (struct msg_lsa_change_notify *) buf; - len = ntohs (data->length) + sizeof (struct msg_lsa_change_notify) - - sizeof (struct lsa_header); nmsg->ifaddr = ifaddr; nmsg->area_id = area_id; nmsg->is_self_originated = is_self_originated; memset (&nmsg->pad, 0, sizeof (nmsg->pad)); - memcpy (&nmsg->data, data, ntohs (data->length)); + + len = ntohs (data->length); + if (len > sizeof (buf) - offsetof (struct msg_lsa_change_notify, data)) + len = sizeof (buf) - offsetof (struct msg_lsa_change_notify, data); + memcpy (&nmsg->data, data, len); + len += sizeof (struct msg_lsa_change_notify) - sizeof (struct lsa_header); return msg_new (msgtype, nmsg, seqnum, len); } diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 84d70ccc..db1ccda7 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -299,13 +299,10 @@ void ospf_apiserver_event (enum event event, int fd, struct ospf_apiserver *apiserv) { - struct thread *apiserver_serv_thread; - switch (event) { case OSPF_APISERVER_ACCEPT: - apiserver_serv_thread = - thread_add_read (master, ospf_apiserver_accept, apiserv, fd); + (void)thread_add_read (master, ospf_apiserver_accept, apiserv, fd); break; case OSPF_APISERVER_SYNC_READ: apiserv->t_sync_read = diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index a23b4f2b..8bef1754 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -44,7 +44,7 @@ #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_dump.h" - + /* Remove external route. */ void ospf_external_route_remove (struct ospf *ospf, struct prefix_ipv4 *p) @@ -96,7 +96,7 @@ ospf_external_route_lookup (struct ospf *ospf, return NULL; } - + /* Add an External info for AS-external-LSA. */ struct external_info * ospf_external_info_new (u_char type) @@ -164,7 +164,8 @@ ospf_external_info_add (u_char type, struct prefix_ipv4 p, new->nexthop = nexthop; new->tag = 0; - rn->info = new; + if (rn) + rn->info = new; if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) zlog_debug ("Redistribute[%s]: %s/%d external info created.", @@ -233,7 +234,7 @@ ospf_external_info_find_lsa (struct ospf *ospf, return lsa; } - + /* Update ASBR status. */ void ospf_asbr_status_update (struct ospf *ospf, u_char status) @@ -263,7 +264,7 @@ ospf_asbr_status_update (struct ospf *ospf, u_char status) } /* Transition from/to status ASBR, schedule timer. */ - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_STATUS_CHANGE); ospf_router_lsa_update (ospf); } diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 6a72e31d..8aedc808 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -636,6 +636,7 @@ ospf_ase_calculate_timer (struct thread *t) struct route_node *rn; struct listnode *node; struct ospf_area *area; + struct timeval start_time, stop_time; ospf = THREAD_ARG (t); ospf->t_ase_calc = NULL; @@ -644,6 +645,8 @@ ospf_ase_calculate_timer (struct thread *t) { ospf->ase_calc = 0; + quagga_gettime(QUAGGA_CLK_MONOTONIC, &start_time); + /* Calculate external route for each AS-external-LSA */ LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) ospf_ase_calculate_route (ospf, lsa); @@ -673,6 +676,12 @@ ospf_ase_calculate_timer (struct thread *t) ospf_route_table_free (ospf->old_external_route); ospf->old_external_route = ospf->new_external_route; ospf->new_external_route = route_table_init (); + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &stop_time); + + zlog_info ("SPF Processing Time(usecs): External Routes: %ld\n", + (stop_time.tv_sec - start_time.tv_sec)*1000000L+ + (stop_time.tv_usec - start_time.tv_usec)); } return 0; } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 7e11e251..ebcc717f 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -143,7 +143,7 @@ unsigned long term_debug_ospf_lsa = 0; unsigned long term_debug_ospf_zebra = 0; unsigned long term_debug_ospf_nssa = 0; - + const char * ospf_redist_string(u_int route_type) @@ -218,7 +218,7 @@ ospf_if_name_string (struct ospf_interface *oi) return buf; } - + void ospf_nbr_state_message (struct ospf_neighbor *nbr, char *buf, size_t size) { @@ -247,16 +247,21 @@ ospf_timeval_dump (struct timeval *t, char *buf, size_t size) #define HOUR_IN_SECONDS (60*MINUTE_IN_SECONDS) #define DAY_IN_SECONDS (24*HOUR_IN_SECONDS) #define WEEK_IN_SECONDS (7*DAY_IN_SECONDS) - unsigned long w, d, h, m, s, ms; + unsigned long w, d, h, m, s, ms, us; if (!t) return "inactive"; - w = d = h = m = s = ms = 0; + w = d = h = m = s = ms = us = 0; memset (buf, 0, size); - - ms = t->tv_usec / 1000; - + + us = t->tv_usec; + if (us >= 1000) + { + ms = us / 1000; + us %= 1000; + } + if (ms >= 1000) { t->tv_sec += ms / 1000; @@ -297,9 +302,11 @@ ospf_timeval_dump (struct timeval *t, char *buf, size_t size) snprintf (buf, size, "%ldh%02ldm%02lds", h, m, t->tv_sec); else if (m) snprintf (buf, size, "%ldm%02lds", m, t->tv_sec); - else + else if (ms) snprintf (buf, size, "%ld.%03lds", t->tv_sec, ms); - + else + snprintf (buf, size, "%ld usecs", t->tv_usec); + return buf; } @@ -746,7 +753,7 @@ ospf_packet_dump (struct stream *s) stream_set_getp (s, gp); } - + /* [no] debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) [send|recv [detail]] @@ -956,7 +963,7 @@ ALIAS (no_debug_ospf_packet, "Packet received\n" "Detail Information\n") - + DEFUN (debug_ospf_ism, debug_ospf_ism_cmd, "debug ospf ism", @@ -1058,7 +1065,7 @@ ALIAS (no_debug_ospf_ism, "ISM Event Information\n" "ISM Timer Information\n") - + DEFUN (debug_ospf_nsm, debug_ospf_nsm_cmd, "debug ospf nsm", @@ -1161,7 +1168,7 @@ ALIAS (no_debug_ospf_nsm, "NSM Event Information\n" "NSM Timer Information\n") - + DEFUN (debug_ospf_lsa, debug_ospf_lsa_cmd, "debug ospf lsa", @@ -1274,7 +1281,7 @@ ALIAS (no_debug_ospf_lsa, "LSA Install/Delete\n" "LSA Refres\n") - + DEFUN (debug_ospf_zebra, debug_ospf_zebra_cmd, "debug ospf zebra", @@ -1366,7 +1373,7 @@ ALIAS (no_debug_ospf_zebra, "OSPF Zebra information\n" "Zebra interface\n" "Zebra redistribute\n") - + DEFUN (debug_ospf_event, debug_ospf_event_cmd, "debug ospf event", @@ -1421,7 +1428,7 @@ DEFUN (no_debug_ospf_nssa, return CMD_SUCCESS; } - + DEFUN (show_debugging_ospf, show_debugging_ospf_cmd, "show debugging ospf", diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 2ebae89a..d18314a9 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_dump.h" extern struct zclient *zclient; - + /* Do the LSA acking specified in table 19, Section 13.5, row 2 * This get called from ospf_flood_out_interface. Declared inline * for speed. */ @@ -757,7 +757,7 @@ ospf_flood_through (struct ospf *ospf, return (lsa_ack_flag); } - + /* Management functions for neighbor's Link State Request list. */ void @@ -835,7 +835,7 @@ ospf_ls_request_new (struct lsa_header *lsah) return new; } - + /* Management functions for neighbor's ls-retransmit list. */ unsigned long ospf_ls_retransmit_count (struct ospf_neighbor *nbr) @@ -973,14 +973,19 @@ ospf_ls_retransmit_delete_nbr_as (struct ospf *ospf, struct ospf_lsa *lsa) ospf_ls_retransmit_delete_nbr_if (oi, lsa); } - + /* Sets ls_age to MaxAge and floods throu the area. When we implement ASE routing, there will be anothe function flushing an LSA from the whole domain. */ void ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area) { + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + lsa->tv_recv = recent_relative_time (); + lsa->tv_orig = lsa->tv_recv; ospf_flood_through_area (area, NULL, lsa); ospf_lsa_maxage (area->ospf, lsa); } @@ -988,7 +993,12 @@ ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area) void ospf_lsa_flush_as (struct ospf *ospf, struct ospf_lsa *lsa) { + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + lsa->tv_recv = recent_relative_time (); + lsa->tv_orig = lsa->tv_recv; ospf_flood_through_as (ospf, NULL, lsa); ospf_lsa_maxage (ospf, lsa); } diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index 6a8c3c63..b2d0faeb 100644 --- a/ospfd/ospf_ia.c +++ b/ospfd/ospf_ia.c @@ -193,7 +193,7 @@ ospf_ia_router_route (struct ospf *ospf, struct route_table *rtrs, listnode_add (rn->info, new_or); } - + static int process_summary_lsa (struct ospf_area *area, struct route_table *rt, struct route_table *rtrs, struct ospf_lsa *lsa) diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index a37dde12..0f02cc82 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_snmp.h" #endif /* HAVE_SNMP */ - + int ospf_if_get_output_cost (struct ospf_interface *oi) { @@ -334,7 +334,7 @@ ospf_if_free (struct ospf_interface *oi) XFREE (MTYPE_OSPF_IF, oi); } - + /* * check if interface with given address is configured and * return it if yes. special treatment for PtP networks. @@ -489,7 +489,7 @@ ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src, return match; } - + void ospf_if_stream_set (struct ospf_interface *oi) { @@ -518,7 +518,7 @@ ospf_if_stream_unset (struct ospf_interface *oi) } } - + static struct ospf_if_params * ospf_new_if_params (void) { @@ -825,7 +825,7 @@ ospf_if_down (struct ospf_interface *oi) return 1; } - + /* Virtual Link related functions. */ struct ospf_vl_data * @@ -1194,7 +1194,7 @@ ospf_vls_in_area (struct ospf_area *area) return c; } - + struct crypt_key * ospf_crypt_key_new () { diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 9de65507..ed698c87 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -140,14 +140,6 @@ struct ospf_interface /* OSPF Network Type. */ u_char type; -#define OSPF_IFTYPE_NONE 0 -#define OSPF_IFTYPE_POINTOPOINT 1 -#define OSPF_IFTYPE_BROADCAST 2 -#define OSPF_IFTYPE_NBMA 3 -#define OSPF_IFTYPE_POINTOMULTIPOINT 4 -#define OSPF_IFTYPE_VIRTUALLINK 5 -#define OSPF_IFTYPE_LOOPBACK 6 -#define OSPF_IFTYPE_MAX 7 /* State of Interface State Machine. */ u_char state; diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index db53882d..9649df8d 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -44,7 +44,7 @@ #include "ospfd/ospf_flood.h" #include "ospfd/ospf_abr.h" #include "ospfd/ospf_snmp.h" - + /* elect DR and BDR. Refer to RFC2319 section 9.4 */ static struct ospf_neighbor * ospf_dr_election_sub (struct list *routers) @@ -203,7 +203,6 @@ ospf_dr_election (struct ospf_interface *oi) struct in_addr old_dr, old_bdr; int old_state, new_state; struct list *el_list; - struct ospf_neighbor *dr, *bdr; /* backup current values. */ old_dr = DR (oi); @@ -216,8 +215,8 @@ ospf_dr_election (struct ospf_interface *oi) ospf_dr_eligible_routers (oi->nbrs, el_list); /* First election of DR and BDR. */ - bdr = ospf_elect_bdr (oi, el_list); - dr = ospf_elect_dr (oi, el_list); + ospf_elect_bdr (oi, el_list); + ospf_elect_dr (oi, el_list); new_state = ospf_ism_state (oi); @@ -246,7 +245,7 @@ ospf_dr_election (struct ospf_interface *oi) return new_state; } - + int ospf_hello_timer (struct thread *thread) { diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index e778251c..94c31c9f 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" - + u_int32_t get_metric (u_char *metric) { @@ -61,7 +61,7 @@ get_metric (u_char *metric) return m; } - + struct timeval tv_adjust (struct timeval a) { @@ -159,7 +159,7 @@ ospf_lsa_refresh_delay (struct ospf_lsa *lsa) return delay; } - + int get_age (struct ospf_lsa *lsa) { @@ -171,7 +171,7 @@ get_age (struct ospf_lsa *lsa) return age; } - + /* Fletcher Checksum -- Refer to RFC1008. */ /* All the offsets are zero-based. The offsets in the RFC1008 are @@ -205,7 +205,7 @@ ospf_lsa_checksum_valid (struct lsa_header *lsa) } - + /* Create OSPF LSA. */ struct ospf_lsa * ospf_lsa_new () @@ -341,7 +341,7 @@ ospf_lsa_data_free (struct lsa_header *lsah) XFREE (MTYPE_OSPF_LSA_DATA, lsah); } - + /* LSA general functions. */ const char * @@ -393,7 +393,7 @@ lsa_header_set (struct stream *s, u_char options, stream_forward_endp (s, OSPF_LSA_HEADER_SIZE); } - + /* router-LSA related functions. */ /* Get router-LSA flags. */ @@ -568,6 +568,9 @@ lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi) /* Describe Type 3 Link. */ if (oi->state == ISM_Waiting) { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s is in state Waiting. " + "Adding stub interface", oi->ifp->name); masklen2ip (oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, @@ -580,12 +583,18 @@ lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi) IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))) && ospf_nbr_count (oi, NSM_Full) > 0) { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s has a DR. " + "Adding transit interface", oi->ifp->name); return link_info_set (s, DR (oi), oi->address->u.prefix4, LSA_LINK_TYPE_TRANSIT, 0, cost); } /* Describe type 3 link. */ else { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s has no DR. " + "Adding stub interface", oi->ifp->name); masklen2ip (oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, @@ -737,7 +746,7 @@ ospf_router_lsa_body_set (struct stream *s, struct ospf_area *area) /* Set # of links here. */ stream_putw_at (s, putp, cnt); } - + static int ospf_stub_router_timer (struct thread *t) { @@ -794,7 +803,7 @@ ospf_stub_router_check (struct ospf_area *area) OSPF_AREA_TIMER_ON (area->t_stub_router, ospf_stub_router_timer, area->ospf->stub_router_startup_time); } - + /* Create new router-LSA. */ static struct ospf_lsa * ospf_router_lsa_new (struct ospf_area *area) @@ -996,7 +1005,7 @@ ospf_router_lsa_update (struct ospf *ospf) return 0; } - + /* network-LSA related functions. */ /* Originate Network-LSA. */ static void @@ -1175,7 +1184,7 @@ ospf_network_lsa_refresh (struct ospf_lsa *lsa) return new; } - + static void stream_put_ospf_metric (struct stream *s, u_int32_t metric_value) { @@ -1334,18 +1343,14 @@ ospf_summary_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) return new; } - + /* summary-ASBR-LSA related functions. */ static void ospf_summary_asbr_lsa_body_set (struct stream *s, struct prefix *p, u_int32_t metric) { - struct in_addr mask; - - masklen2ip (p->prefixlen, &mask); - /* Put Network Mask. */ - stream_put_ipv4 (s, mask.s_addr); + stream_put_ipv4 (s, (u_int32_t) 0); /* Set # TOS. */ stream_putc (s, (u_char) 0); @@ -2372,7 +2377,7 @@ ospf_external_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa, return new; } - + /* LSA installation functions. */ /* Install router-LSA to an area. */ @@ -2404,8 +2409,7 @@ ospf_router_lsa_install (struct ospf *ospf, struct ospf_lsa *new, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - ospf_spf_calculate_schedule (ospf); - + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ROUTER_LSA_INSTALL); return new; } @@ -2439,7 +2443,7 @@ ospf_network_lsa_install (struct ospf *ospf, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_NETWORK_LSA_INSTALL); return new; } @@ -2462,11 +2466,9 @@ ospf_summary_lsa_install (struct ospf *ospf, struct ospf_lsa *new, /* This doesn't exist yet... */ ospf_summary_incremental_update(new); */ #else /* #if 0 */ - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_SUMMARY_LSA_INSTALL); #endif /* #if 0 */ - if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) - zlog_debug ("ospf_summary_lsa_install(): SPF scheduled"); } if (IS_LSA_SELF (new)) @@ -2495,7 +2497,7 @@ ospf_summary_asbr_lsa_install (struct ospf *ospf, struct ospf_lsa *new, - RFC 2328 Section 16.5 implies it should be */ /* ospf_ase_calculate_schedule(); */ #else /* #if 0 */ - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); #endif /* #if 0 */ } @@ -2737,7 +2739,9 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, if (IS_LSA_SELF (lsa)) lsa->oi = oi; /* Specify outgoing ospf-interface for this LSA. */ else - ; /* Incoming "oi" for this LSA has set at LSUpd reception. */ + { + /* Incoming "oi" for this LSA has set at LSUpd reception. */ + } /* Fallthrough */ case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: @@ -2782,22 +2786,21 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, If received LSA' ls_age is MaxAge, or lsa is being prematurely aged (it's getting flushed out of the area), set LSA on MaxAge LSA list. */ - if ((lsa->flags & OSPF_LSA_PREMATURE_AGE) || - (IS_LSA_MAXAGE (new) && !IS_LSA_SELF (new))) + if (IS_LSA_MAXAGE (new)) { if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) zlog_debug ("LSA[Type%d:%s]: Install LSA 0x%p, MaxAge", new->data->type, inet_ntoa (new->data->id), lsa); - ospf_lsa_flush (ospf, lsa); + ospf_lsa_maxage (ospf, lsa); } return new; } - -static int + +int ospf_check_nbr_status (struct ospf *ospf) { struct listnode *node, *nnode; @@ -2821,14 +2824,14 @@ ospf_check_nbr_status (struct ospf *ospf) return 1; } - + static int ospf_maxage_lsa_remover (struct thread *thread) { struct ospf *ospf = THREAD_ARG (thread); struct ospf_lsa *lsa; - struct listnode *node, *nnode; + struct route_node *rn; int reschedule = 0; ospf->t_maxage = NULL; @@ -2839,8 +2842,16 @@ ospf_maxage_lsa_remover (struct thread *thread) reschedule = !ospf_check_nbr_status (ospf); if (!reschedule) - for (ALL_LIST_ELEMENTS (ospf->maxage_lsa, node, nnode, lsa)) + for (rn = route_top(ospf->maxage_lsa); rn; rn = route_next(rn)) { + if ((lsa = rn->info) == NULL) + { + continue; + } + + /* There is at least one neighbor from which we still await an ack + * for that LSA, so we are not allowed to remove it from our lsdb yet + * as per RFC 2328 section 14 para 4 a) */ if (lsa->retransmit_counter > 0) { reschedule = 1; @@ -2849,7 +2860,11 @@ ospf_maxage_lsa_remover (struct thread *thread) /* TODO: maybe convert this function to a work-queue */ if (thread_should_yield (thread)) - OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0); + { + OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0); + route_unlock_node(rn); /* route_top/route_next */ + return 0; + } /* Remove LSA from the LSDB */ if (IS_LSA_SELF (lsa)) @@ -2893,13 +2908,24 @@ ospf_maxage_lsa_remover (struct thread *thread) void ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) { - struct listnode *n; + struct route_node *rn; + struct prefix_ptr lsa_prefix; + + lsa_prefix.family = 0; + lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT; + lsa_prefix.prefix = (uintptr_t) lsa; - if ((n = listnode_lookup (ospf->maxage_lsa, lsa))) + if ((rn = route_node_lookup(ospf->maxage_lsa, + (struct prefix *)&lsa_prefix))) { - list_delete_node (ospf->maxage_lsa, n); - UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); - ospf_lsa_unlock (&lsa); /* maxage_lsa */ + if (rn->info == lsa) + { + UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + ospf_lsa_unlock (&lsa); /* maxage_lsa */ + rn->info = NULL; + route_unlock_node (rn); /* unlock node because lsa is deleted */ + } + route_unlock_node (rn); /* route_node_lookup */ } } @@ -2911,6 +2937,9 @@ ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) void ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) { + struct prefix_ptr lsa_prefix; + struct route_node *rn; + /* When we saw a MaxAge LSA flooded to us, we put it on the list and schedule the MaxAge LSA remover. */ if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) @@ -2921,8 +2950,31 @@ ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) return; } - listnode_add (ospf->maxage_lsa, ospf_lsa_lock (lsa)); - SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + lsa_prefix.family = 0; + lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT; + lsa_prefix.prefix = (uintptr_t) lsa; + + if ((rn = route_node_get (ospf->maxage_lsa, + (struct prefix *)&lsa_prefix)) != NULL) + { + if (rn->info != NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA[%s]: found LSA (%p) in table for LSA %p %d", + dump_lsa_key (lsa), rn->info, lsa, lsa_prefix.prefixlen); + route_unlock_node (rn); + } + else + { + rn->info = ospf_lsa_lock(lsa); + SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + } + } + else + { + zlog_err("Unable to allocate memory for maxage lsa\n"); + assert(0); + } if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) zlog_debug ("LSA[%s]: MaxAge LSA remover scheduled.", dump_lsa_key (lsa)); @@ -2967,7 +3019,7 @@ ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) ospf_ase_incremental_update (ospf, lsa); break; default: - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_MAXAGE); break; } ospf_lsa_maxage (ospf, lsa); @@ -3484,7 +3536,7 @@ ospf_lsa_unique_id (struct ospf *ospf, return id; } - + #define LSA_ACTION_FLOOD_AREA 1 #define LSA_ACTION_FLUSH_AREA 2 @@ -3547,7 +3599,7 @@ ospf_schedule_lsa_flush_area (struct ospf_area *area, struct ospf_lsa *lsa) thread_add_event (master, ospf_lsa_action, data, 0); } - + /* LSA Refreshment functions. */ struct ospf_lsa * ospf_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 6c95ff17..c71877da 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -243,6 +243,7 @@ extern int tv_cmp (struct timeval, struct timeval); extern int get_age (struct ospf_lsa *); extern u_int16_t ospf_lsa_checksum (struct lsa_header *); +extern int ospf_lsa_checksum_valid (struct lsa_header *); extern int ospf_lsa_refresh_delay (struct ospf_lsa *); extern const char *dump_lsa_key (struct ospf_lsa *); @@ -250,6 +251,7 @@ extern u_int32_t lsa_seqnum_increment (struct ospf_lsa *); extern void lsa_header_set (struct stream *, u_char, u_char, struct in_addr, struct in_addr); extern struct ospf_neighbor *ospf_nbr_lookup_ptop (struct ospf_interface *); +extern int ospf_check_nbr_status (struct ospf *); /* Prototype for LSA primitive. */ extern struct ospf_lsa *ospf_lsa_new (void); diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index ea9a3528..f7cf60fd 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -31,7 +31,7 @@ #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" - + struct ospf_lsdb * ospf_lsdb_new () { @@ -72,13 +72,16 @@ ospf_lsdb_cleanup (struct ospf_lsdb *lsdb) route_table_finish (lsdb->type[i].db); } -static void -lsdb_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa) +void +ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa) { - lp->family = 0; - lp->prefixlen = 64; - lp->id = lsa->data->id; - lp->adv_router = lsa->data->adv_router; + if (lp && lsa && lsa->data) + { + lp->family = 0; + lp->prefixlen = 64; + lp->id = lsa->data->id; + lp->adv_router = lsa->data->adv_router; + } } static void @@ -115,7 +118,7 @@ ospf_lsdb_add (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) struct route_node *rn; table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); rn = route_node_get (table, (struct prefix *)&lp); /* nothing to do? */ @@ -167,7 +170,7 @@ ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) assert (lsa->data->type < OSPF_MAX_LSA); table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); if ((rn = route_node_lookup (table, (struct prefix *) &lp))) { if (rn->info == lsa) @@ -218,7 +221,7 @@ ospf_lsdb_lookup (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) struct ospf_lsa *find; table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); rn = route_node_lookup (table, (struct prefix *) &lp); if (rn) { diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h index 4157b685..51ae45bf 100644 --- a/ospfd/ospf_lsdb.h +++ b/ospfd/ospf_lsdb.h @@ -66,6 +66,7 @@ extern struct ospf_lsdb *ospf_lsdb_new (void); extern void ospf_lsdb_init (struct ospf_lsdb *); extern void ospf_lsdb_free (struct ospf_lsdb *); extern void ospf_lsdb_cleanup (struct ospf_lsdb *); +extern void ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa); extern void ospf_lsdb_add (struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete (struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete_all (struct ospf_lsdb *); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 1448c7d8..96dfd579 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -132,7 +132,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -174,7 +174,7 @@ struct quagga_signal_t ospf_signals[] = .handler = &sigint, }, }; - + /* OSPFd main routine. */ int main (int argc, char **argv) @@ -191,6 +191,11 @@ main (int argc, char **argv) /* Set umask before anything for security */ umask (0027); +#ifdef SUPPORT_OSPF_API + /* OSPF apiserver is disabled by default. */ + ospf_apiserver_enable = 0; +#endif /* SUPPORT_OSPF_API */ + /* get program name */ progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); @@ -275,11 +280,6 @@ main (int argc, char **argv) /* OSPF master init. */ ospf_master_init (); -#ifdef SUPPORT_OSPF_API - /* OSPF apiserver is disabled by default. */ - ospf_apiserver_enable = 0; -#endif /* SUPPORT_OSPF_API */ - /* Initializations. */ master = om->master; @@ -310,7 +310,14 @@ main (int argc, char **argv) ospf_opaque_init (); #endif /* HAVE_OPAQUE_LSA */ - sort_node (); + /* Need to initialize the default ospf structure, so the interface mode + commands can be duly processed if they are received before 'router ospf', + when quagga(ospfd) is restarted */ + if (!ospf_get()) + { + zlog_err("OSPF instance init failed: %s", strerror(errno)); + exit (1); + } /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 3e326a8c..900a5667 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -228,7 +228,7 @@ ospf_sock_init (void) } void -ospf_adjust_sndbuflen (struct ospf * ospf, int buflen) +ospf_adjust_sndbuflen (struct ospf * ospf, unsigned int buflen) { int ret, newbuflen; /* Check if any work has to be done at all. */ @@ -249,11 +249,11 @@ ospf_adjust_sndbuflen (struct ospf * ospf, int buflen) */ ret = setsockopt_so_sendbuf (ospf->fd, buflen); newbuflen = getsockopt_so_sendbuf (ospf->fd); - if (ret < 0 || newbuflen < buflen) - zlog_warn ("%s: tried to set SO_SNDBUF to %d, but got %d", + if (ret < 0 || newbuflen < 0 || newbuflen < (int) buflen) + zlog_warn ("%s: tried to set SO_SNDBUF to %u, but got %d", __func__, buflen, newbuflen); if (newbuflen >= 0) - ospf->maxsndbuflen = newbuflen; + ospf->maxsndbuflen = (unsigned int)newbuflen; else zlog_warn ("%s: failed to get SO_SNDBUF", __func__); if (ospfd_privs.change (ZPRIVS_LOWER)) diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index f6909912..e0a5c69d 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -34,6 +34,6 @@ extern int ospf_if_drop_alldrouters (struct ospf *, struct prefix *, unsigned int); extern int ospf_if_ipmulticast (struct ospf *, struct prefix *, unsigned int); extern int ospf_sock_init (void); -extern void ospf_adjust_sndbuflen (struct ospf *, int); +extern void ospf_adjust_sndbuflen (struct ospf *, unsigned int); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index cbc31716..0e6814e2 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_snmp.h" static void nsm_clear_adj (struct ospf_neighbor *); - + /* OSPF NSM Timer functions. */ static int ospf_inactivity_timer (struct thread *thread) @@ -72,14 +72,11 @@ ospf_inactivity_timer (struct thread *thread) static int ospf_db_desc_timer (struct thread *thread) { - struct ospf_interface *oi; struct ospf_neighbor *nbr; nbr = THREAD_ARG (thread); nbr->t_db_desc = NULL; - oi = nbr->oi; - if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (DD Retransmit timer expire)", IF_NAME (nbr->oi), inet_ntoa (nbr->src)); @@ -159,7 +156,7 @@ nsm_should_adj (struct ospf_neighbor *nbr) return 0; } - + /* OSPF NSM functions. */ static int nsm_packet_received (struct ospf_neighbor *nbr) @@ -261,7 +258,7 @@ ospf_db_summary_clear (struct ospf_neighbor *nbr) } } - + /* The area link state database consists of the router-LSAs, network-LSAs and summary-LSAs contained in the area structure, @@ -643,7 +640,7 @@ nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event) #endif } -void +static void nsm_change_state (struct ospf_neighbor *nbr, int state) { struct ospf_interface *oi = nbr->oi; @@ -664,6 +661,25 @@ nsm_change_state (struct ospf_neighbor *nbr, int state) if (oi->type == OSPF_IFTYPE_VIRTUALLINK) vl_area = ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id); + /* Generate NeighborChange ISM event. + * + * In response to NeighborChange, DR election is rerun. The information + * from the election process is required by the router-lsa construction. + * + * Therefore, trigger the event prior to refreshing the LSAs. */ + switch (oi->state) { + case ISM_DROther: + case ISM_Backup: + case ISM_DR: + if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) || + (old_state >= NSM_TwoWay && state < NSM_TwoWay)) + OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange); + break; + default: + /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */ + break; + } + /* One of the neighboring routers changes to/from the FULL state. */ if ((old_state != NSM_Full && state == NSM_Full) || (old_state == NSM_Full && state != NSM_Full)) @@ -763,20 +779,6 @@ nsm_change_state (struct ospf_neighbor *nbr, int state) if (state == NSM_Down) nbr->crypt_seqnum = 0; - /* Generete NeighborChange ISM event. */ - switch (oi->state) { - case ISM_DROther: - case ISM_Backup: - case ISM_DR: - if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) || - (old_state >= NSM_TwoWay && state < NSM_TwoWay)) - OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange); - break; - default: - /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */ - break; - } - /* Preserve old status? */ } @@ -787,11 +789,9 @@ ospf_nsm_event (struct thread *thread) int event; int next_state; struct ospf_neighbor *nbr; - struct in_addr router_id; nbr = THREAD_ARG (thread); event = THREAD_VAL (thread); - router_id = nbr->router_id; if (IS_DEBUG_OSPF (nsm, NSM_EVENTS)) zlog_debug ("NSM[%s:%s]: %s (%s)", IF_NAME (nbr->oi), diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index 4f2ae808..9b7e14a4 100644 --- a/ospfd/ospf_nsm.h +++ b/ospfd/ospf_nsm.h @@ -81,7 +81,6 @@ /* Prototypes. */ extern int ospf_nsm_event (struct thread *); -extern void nsm_change_state (struct ospf_neighbor *, int); extern void ospf_check_nbr_loading (struct ospf_neighbor *); extern int ospf_db_summary_isempty (struct ospf_neighbor *); extern int ospf_db_summary_count (struct ospf_neighbor *); diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index aa126e19..ecb28ffe 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -223,9 +223,15 @@ ospf_opaque_type_name (u_char opaque_type) default: if (OPAQUE_TYPE_RANGE_UNASSIGNED (opaque_type)) name = "Unassigned"; - /* XXX warning: comparison is always true due to limited range of data type */ - else if (OPAQUE_TYPE_RANGE_RESERVED (opaque_type)) - name = "Private/Experimental"; + else + { + u_int32_t bigger_range = opaque_type; + /* + * Get around type-limits warning: comparison is always true due to limited range of data type + */ + if (OPAQUE_TYPE_RANGE_RESERVED (bigger_range)) + name = "Private/Experimental"; + } break; } return name; @@ -2120,10 +2126,12 @@ ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) *------------------------------------------------------------------------*/ static void ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, struct ospf_neighbor *inbr, struct ospf_lsa *lsa); +#ifdef BUGGY_UNLOCK static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi); static void ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area); static void ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top); static unsigned long ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type); +#endif /* BUGGY_UNLOCK */ void ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, struct list *lsas) @@ -2290,17 +2298,20 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) { case OSPF_OPAQUE_LINK_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT)) - ospf_opaque_type9_lsa_rxmt_nbr_check (nbr->oi); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type9_lsa_rxmt_nbr_check (nbr->oi); */ /* Callback function... */ break; case OSPF_OPAQUE_AREA_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT)) - ospf_opaque_type10_lsa_rxmt_nbr_check (nbr->oi->area); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type10_lsa_rxmt_nbr_check (nbr->oi->area); */ /* Callback function... */ break; case OSPF_OPAQUE_AS_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT)) - ospf_opaque_type11_lsa_rxmt_nbr_check (top); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type11_lsa_rxmt_nbr_check (top); */ /* Callback function... */ break; default: @@ -2309,11 +2320,10 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) } if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Block Opaque-LSA origination: ON -> OFF"); return; /* Blocking still in progress. */ - } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Block Opaque-LSA origination: ON -> OFF"); if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) return; /* Opaque capability condition must have changed. */ @@ -2333,6 +2343,7 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) return; } +#ifdef BUGGY_UNLOCK static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi) { @@ -2433,6 +2444,7 @@ ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type) return n; } +#endif /* BUGGY_UNLOCK */ /*------------------------------------------------------------------------* * Followings are util functions; probably be used by Opaque-LSAs only... diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index ede59088..98b1af3b 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -323,7 +323,7 @@ ospf_packet_max (struct ospf_interface *oi) return max; } - + static int ospf_check_md5_digest (struct ospf_interface *oi, struct ospf_header *ospfh) { @@ -383,7 +383,7 @@ static int ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) { struct ospf_header *ospfh; - unsigned char digest[OSPF_AUTH_MD5_SIZE]; + unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; MD5_CTX ctx; void *ibuf; u_int32_t t; @@ -410,7 +410,7 @@ ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) /* Get MD5 Authentication key from auth_key list. */ if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) - auth_key = (const u_int8_t *) ""; + auth_key = (const u_int8_t *) digest; else { ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt))); @@ -438,7 +438,7 @@ ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) return OSPF_AUTH_MD5_SIZE; } - + static int ospf_ls_req_timer (struct thread *thread) { @@ -789,6 +789,9 @@ ospf_write (struct thread *thread) /* Now delete packet from queue. */ ospf_packet_delete (oi); + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + listnode_move_to_tail (ospf->oi_write_q, node); if (ospf_fifo_head (oi->obuf) == NULL) { oi->on_write_q = 0; @@ -1660,8 +1663,10 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, case OSPF_AS_EXTERNAL_LSA: #ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: +#endif /* HAVE_OPAQUE_LSA */ lsa->area = NULL; break; +#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: lsa->oi = oi; /* Remember incoming interface for flooding control. */ /* Fallthrough */ @@ -1761,7 +1766,11 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, ospf_lsa_discard (L); \ continue; } - /* Process each LSA received in the one packet. */ + /* Process each LSA received in the one packet. + * + * Numbers in parentheses, e.g. (1), (2), etc., and the corresponding + * text below are from the steps in RFC 2328, Section 13. + */ for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa)) { struct ospf_lsa *ls_ret, *current; @@ -1785,11 +1794,11 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, listnode_delete (lsas, lsa); /* We don't need it in list anymore */ - /* Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ + /* (1) Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ - /* LSA Type - Done above by ospf_ls_upd_list_lsa() */ + /* (2) LSA Type - Done above by ospf_ls_upd_list_lsa() */ - /* Do not take in AS External LSAs if we are a stub or NSSA. */ + /* (3) Do not take in AS External LSAs if we are a stub or NSSA. */ /* Do not take in AS NSSA if this neighbor and we are not NSSA */ @@ -1817,25 +1826,48 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, DISCARD_LSA (lsa,2); } + /* VU229804: Router-LSA Adv-ID must be equal to LS-ID */ + if (lsa->data->type == OSPF_ROUTER_LSA) + if (!IPV4_ADDR_SAME(&lsa->data->id, &lsa->data->adv_router)) + { + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + char buf3[INET_ADDRSTRLEN]; + + zlog_err("Incoming Router-LSA from %s with " + "Adv-ID[%s] != LS-ID[%s]", + inet_ntop (AF_INET, &ospfh->router_id, + buf1, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->id, + buf2, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->adv_router, + buf3, INET_ADDRSTRLEN)); + zlog_err("OSPF domain compromised by attack or corruption. " + "Verify correct operation of -ALL- OSPF routers."); + DISCARD_LSA (lsa, 0); + } + /* Find the LSA in the current database. */ current = ospf_lsa_lookup_by_header (oi->area, lsa->data); - /* If the LSA's LS age is equal to MaxAge, and there is currently + /* (4) If the LSA's LS age is equal to MaxAge, and there is currently no instance of the LSA in the router's link state database, and none of router's neighbors are in states Exchange or Loading, - then take the following actions. */ + then take the following actions: */ if (IS_LSA_MAXAGE (lsa) && !current && - (ospf_nbr_count (oi, NSM_Exchange) + - ospf_nbr_count (oi, NSM_Loading)) == 0) + ospf_check_nbr_status(oi->ospf)) { - /* Response Link State Acknowledgment. */ + /* (4a) Response Link State Acknowledgment. */ ospf_ls_ack_send (nbr, lsa); - /* Discard LSA. */ - zlog_info ("Link State Update[%s]: LS age is equal to MaxAge.", - dump_lsa_key(lsa)); + /* (4b) Discard LSA. */ + if (IS_DEBUG_OSPF (lsa, LSA)) + { + zlog_debug ("Link State Update[%s]: LS age is equal to MaxAge.", + dump_lsa_key(lsa)); + } DISCARD_LSA (lsa, 3); } @@ -1890,7 +1922,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, #endif /* HAVE_OPAQUE_LSA */ /* It might be happen that received LSA is self-originated network LSA, but - * router ID is cahnged. So, we should check if LSA is a network-LSA whose + * router ID is changed. So, we should check if LSA is a network-LSA whose * Link State ID is one of the router's own IP interface addresses but whose * Advertising Router is not equal to the router's own Router ID * According to RFC 2328 12.4.2 and 13.4 this LSA should be flushed. @@ -1929,7 +1961,9 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, /* (5) Find the instance of this LSA that is currently contained in the router's link state database. If there is no database copy, or the received LSA is more recent than - the database copy the following steps must be performed. */ + the database copy the following steps must be performed. + (The sub steps from RFC 2328 section 13 step (5) will be performed in + ospf_flood() ) */ if (current == NULL || (ret = ospf_lsa_more_recent (current, lsa)) < 0) @@ -2089,7 +2123,7 @@ ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, lsr = ospf_ls_retransmit_lookup (nbr, lsa); - if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum) + if (lsr != NULL && ospf_lsa_more_recent (lsr, lsa) == 0) { #ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsr->data->type)) @@ -2105,7 +2139,7 @@ ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, return; } - + static struct stream * ospf_recv_packet (int fd, struct interface **ifp, struct stream *ibuf) { @@ -3109,22 +3143,7 @@ ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, options = OPTIONS (oi); #ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) - { - if (IS_SET_DD_I (nbr->dd_flags) - || CHECK_FLAG (nbr->options, OSPF_OPTION_O)) - /* - * Set O-bit in the outgoing DD packet for capablity negotiation, - * if one of following case is applicable. - * - * 1) WaitTimer expiration event triggered the neighbor state to - * change to Exstart, but no (valid) DD packet has received - * from the neighbor yet. - * - * 2) At least one DD packet with O-bit on has received from the - * neighbor. - */ - SET_FLAG (options, OSPF_OPTION_O); - } + SET_FLAG (options, OSPF_OPTION_O); #endif /* HAVE_OPAQUE_LSA */ stream_putc (s, options); @@ -3295,7 +3314,7 @@ ospf_make_ls_upd (struct ospf_interface *oi, struct list *update, struct stream u_int16_t ls_age; if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_make_ls_upd: List Iteration"); + zlog_debug ("ospf_make_ls_upd: List Iteration %d", count); lsa = listgetdata (node); @@ -3701,7 +3720,8 @@ ospf_ls_upd_queue_send (struct ospf_interface *oi, struct list *update, u_int16_t length = OSPF_HEADER_SIZE; if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("listcount = %d, dst %s", listcount (update), inet_ntoa(addr)); + zlog_debug ("listcount = %d, [%s]dst %s", listcount (update), IF_NAME(oi), + inet_ntoa(addr)); op = ospf_ls_upd_packet_new (update, oi); diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index c3acba34..eb7829ac 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -126,6 +126,31 @@ ospf_route_table_free (struct route_table *rt) route_table_finish (rt); } +/* If a prefix exists in the new routing table, then return 1, + otherwise return 0. Since the ZEBRA-RIB does an implicit + withdraw, it is not necessary to send a delete, an add later + will act like an implicit delete. */ +static int +ospf_route_exist_new_table (struct route_table *rt, struct prefix_ipv4 *prefix) +{ + struct route_node *rn; + + assert (rt); + assert (prefix); + + rn = route_node_lookup (rt, (struct prefix *) prefix); + if (!rn) { + return 0; + } + route_unlock_node (rn); + + if (!rn->info) { + return 0; + } + + return 1; +} + /* If a prefix and a nexthop match any route in the routing table, then return 1, otherwise return 0. */ int @@ -223,13 +248,13 @@ ospf_route_delete_uniq (struct route_table *rt, struct route_table *cmprt) { if (or->type == OSPF_DESTINATION_NETWORK) { - if (! ospf_route_match_same (cmprt, - (struct prefix_ipv4 *) &rn->p, or)) + if (! ospf_route_exist_new_table (cmprt, + (struct prefix_ipv4 *) &rn->p)) ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or); } else if (or->type == OSPF_DESTINATION_DISCARD) - if (! ospf_route_match_same (cmprt, - (struct prefix_ipv4 *) &rn->p, or)) + if (! ospf_route_exist_new_table (cmprt, + (struct prefix_ipv4 *) &rn->p)) ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p); } } diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index adf81583..d0ebce66 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -443,12 +443,16 @@ static void * route_set_metric_compile (const char *arg) { u_int32_t *metric; + int32_t ret; metric = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); - *metric = atoi (arg); + ret = atoi (arg); - if (*metric >= 0) - return metric; + if (ret >= 0) + { + *metric = (u_int32_t)ret; + return metric; + } XFREE (MTYPE_ROUTE_MAP_COMPILED, metric); return NULL; diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 1daf0d6a..7f7b1578 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -48,7 +48,7 @@ #include "ospfd/ospf_ism.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_snmp.h" - + /* OSPF2-MIB. */ #define OSPF2MIB 1,3,6,1,2,1,14 @@ -204,7 +204,7 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES @@ -501,7 +501,7 @@ struct variable ospf_variables[] = {OSPFAREAAGGREGATEEFFECT, INTEGER, RWRITE, ospfAreaAggregateEntry, 3, {14, 1, 6}} }; - + /* The administrative status of OSPF. When OSPF is enbled on at least one interface return 1. */ static int @@ -1407,7 +1407,7 @@ ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct list *ospf_snmp_iflist; struct ospf_snmp_if @@ -1911,7 +1911,7 @@ ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct route_table *ospf_snmp_vl_table; void @@ -2127,7 +2127,7 @@ ospfVirtIfEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static struct ospf_neighbor * ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr, unsigned int *ifindex) @@ -2360,7 +2360,7 @@ ospfNbrEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static u_char * ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -2419,7 +2419,7 @@ ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static struct ospf_lsa * ospfExtLsdbLookup (struct variable *v, oid *name, size_t *length, u_char *type, struct in_addr *ls_id, struct in_addr *router_id, int exact) @@ -2572,7 +2572,7 @@ ospfExtLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static u_char * ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -2608,7 +2608,7 @@ ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, } return NULL; } - + /* OSPF Traps. */ #define IFSTATECHANGE 16 #define VIRTIFSTATECHANGE 1 diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index a5242b68..1fe8ab4b 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -46,13 +46,56 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "ospfd/ospf_abr.h" #include "ospfd/ospf_dump.h" +/* Variables to ensure a SPF scheduled log message is printed only once */ + +static unsigned int spf_reason_flags = 0; + +static void +ospf_clear_spf_reason_flags () +{ + spf_reason_flags = 0; +} + +static void +ospf_spf_set_reason (ospf_spf_reason_t reason) +{ + spf_reason_flags |= 1 << reason; +} + +static void +ospf_get_spf_reason_str (char *buf) +{ + if (!buf) + return; + + buf[0] = '\0'; + if (spf_reason_flags) + { + if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL) + strcat (buf, "R, "); + if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL) + strcat (buf, "N, "); + if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL) + strcat (buf, "S, "); + if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL) + strcat (buf, "AS, "); + if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE) + strcat (buf, "ABR, "); + if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE) + strcat (buf, "ASBR, "); + if (spf_reason_flags & SPF_FLAG_MAXAGE) + strcat (buf, "M, "); + buf[strlen(buf)-2] = '\0'; /* skip the last ", " */ + } +} + static void ospf_vertex_free (void *); /* List of allocated vertices, to simplify cleanup of SPF. * Not thread-safe obviously. If it ever needs to be, it'd have to be * dynamically allocated at begin of ospf_spf_calculate */ static struct list vertex_list = { .del = ospf_vertex_free }; - + /* Heap related functions, for the managment of the candidates, to * be used with pqueue. */ static int @@ -90,7 +133,7 @@ update_stat (void *node , int position) /* Set the status of the vertex, when its position changes. */ *(v->stat) = position; } - + static struct vertex_nexthop * vertex_nexthop_new (void) { @@ -134,7 +177,7 @@ ospf_canonical_nexthops_free (struct vertex *root) vertex_nexthop_free (vp->nexthop); } } - + /* TODO: Parent list should be excised, in favour of maintaining only * vertex_nexthop, with refcounts. */ @@ -159,7 +202,7 @@ vertex_parent_free (void *p) { XFREE (MTYPE_OSPF_VERTEX_PARENT, p); } - + static struct vertex * ospf_vertex_new (struct ospf_lsa *lsa) { @@ -276,7 +319,7 @@ ospf_vertex_add_parent (struct vertex *v) listnode_add (vp->parent->children, v); } } - + static void ospf_spf_init (struct ospf_area *area) { @@ -568,8 +611,22 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, */ if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - added = 1; - nexthop.s_addr = 0; /* Nexthop not required */ + /* Having nexthop = 0 is tempting, but NOT acceptable. + It breaks AS-External routes with a forwarding address, + since ospf_ase_complete_direct_routes() will mistakenly + assume we've reached the last hop and should place the + forwarding address as nexthop. + Also, users may configure multi-access links in p2p mode, + so we need the IP to ARP the nexthop. + */ + struct ospf_neighbor *nbr_w; + + nbr_w = ospf_nbr_lookup_by_routerid (oi->nbrs, &l->link_id); + if (nbr_w != NULL) + { + added = 1; + nexthop = nbr_w->src; + } } else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { @@ -1218,29 +1275,30 @@ ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, /* Free candidate queue. */ pqueue_delete (candidate); - + ospf_vertex_dump (__func__, area->spf, 0, 1); /* Free nexthop information, canonical versions of which are attached * the first level of router vertices attached to the root vertex, see * ospf_nexthop_calculation. */ ospf_canonical_nexthops_free (area->spf); - - /* Free SPF vertices, but not the list. List has ospf_vertex_free - * as deconstructor. - */ - list_delete_all_node (&vertex_list); - + /* Increment SPF Calculation Counter. */ area->spf_calculation++; quagga_gettime (QUAGGA_CLK_MONOTONIC, &area->ospf->ts_spf); + area->ts_spf = area->ospf->ts_spf; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_spf_calculate: Stop. %ld vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); + + /* Free SPF vertices, but not the list. List has ospf_vertex_free + * as deconstructor. + */ + list_delete_all_node (&vertex_list); } - + /* Timer for SPF calculation. */ static int ospf_spf_calculate_timer (struct thread *thread) @@ -1249,12 +1307,18 @@ ospf_spf_calculate_timer (struct thread *thread) struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; - + struct timeval start_time, stop_time, spf_start_time; + int areas_processed = 0; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ + if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: Timer (SPF calculation expire)"); ospf->t_spf_calc = NULL; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &spf_start_time); /* Allocate new table tree. */ new_table = route_table_init (); new_rtrs = route_table_init (); @@ -1269,21 +1333,36 @@ ospf_spf_calculate_timer (struct thread *thread) */ if (ospf->backbone && ospf->backbone == area) continue; - + ospf_spf_calculate (area, new_table, new_rtrs); + areas_processed++; } - + /* SPF for backbone, if required */ if (ospf->backbone) - ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); - + { + ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); + areas_processed++; + } + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf_vl_shut_unapproved (ospf); + start_time = stop_time; /* saving a call */ + ospf_ia_routing (ospf, new_table, new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + ia_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); ospf_prune_unreachable_networks (new_table); ospf_prune_unreachable_routers (new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + prune_time = timeval_elapsed (stop_time, start_time); /* AS-external-LSA calculation should not be performed here. */ /* If new Router Route is installed, @@ -1293,9 +1372,13 @@ ospf_spf_calculate_timer (struct thread *thread) ospf_ase_calculate_timer_add (ospf); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); + /* Update routing table. */ ospf_route_install (ospf, new_table); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + rt_time = timeval_elapsed (stop_time, start_time); /* Update ABR/ASBR routing table */ if (ospf->old_rtrs) { @@ -1307,11 +1390,34 @@ ospf_spf_calculate_timer (struct thread *thread) ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); if (IS_OSPF_ABR (ospf)) ospf_abr_task (ospf); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + abr_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + total_spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf->ts_spf_duration.tv_sec = total_spf_time/1000000; + ospf->ts_spf_duration.tv_usec = total_spf_time % 1000000; + + ospf_get_spf_reason_str (rbuf); + if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("SPF: calculation complete"); + { + zlog_info ("SPF Processing Time(usecs): %ld", total_spf_time); + zlog_info ("\t SPF Time: %ld", spf_time); + zlog_info ("\t InterArea: %ld", ia_time); + zlog_info ("\t Prune: %ld", prune_time); + zlog_info ("\tRouteInstall: %ld", rt_time); + if (IS_OSPF_ABR (ospf)) + zlog_info ("\t ABR: %ld (%d areas)", + abr_time, areas_processed); + zlog_info ("Reason(s) for SPF: %s", rbuf); + } + + ospf_clear_spf_reason_flags (); return 0; } @@ -1319,7 +1425,7 @@ ospf_spf_calculate_timer (struct thread *thread) /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer for SPF calc. */ void -ospf_spf_calculate_schedule (struct ospf *ospf) +ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; struct timeval result; @@ -1331,6 +1437,8 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (ospf == NULL) return; + ospf_spf_set_reason (reason); + /* SPF calculation timer is already scheduled. */ if (ospf->t_spf_calc) { @@ -1375,6 +1483,8 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer delay = %ld", delay); + zlog_info ("SPF: Scheduled in %ld msec", delay); + ospf->t_spf_calc = thread_add_timer_msec (master, ospf_spf_calculate_timer, ospf, delay); } diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index c1316e4c..e33b3e5f 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -59,9 +59,20 @@ struct vertex_parent int backlink; /* index back to parent for router-lsa's */ }; -extern void ospf_spf_calculate_schedule (struct ospf *); +/* What triggered the SPF ? */ +typedef enum { + SPF_FLAG_ROUTER_LSA_INSTALL = 1, + SPF_FLAG_NETWORK_LSA_INSTALL, + SPF_FLAG_SUMMARY_LSA_INSTALL, + SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL, + SPF_FLAG_MAXAGE, + SPF_FLAG_ABR_STATUS_CHANGE, + SPF_FLAG_ASBR_STATUS_CHANGE, + SPF_FLAG_CONFIG_CHANGE, +} ospf_spf_reason_t; + +extern void ospf_spf_calculate_schedule (struct ospf *, ospf_spf_reason_t); extern void ospf_rtrs_free (struct route_table *); /* void ospf_spf_calculate_timer_add (); */ - #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 24e81052..c605ce68 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -205,7 +205,7 @@ get_mpls_te_instance_value (void) { static u_int32_t seqno = 0; - if (LEGAL_TE_INSTANCE_RANGE (seqno + 1)) + if (seqno < MAX_LEGAL_TE_INSTANCE_NUM ) seqno += 1; else seqno = 1; /* Avoid zero. */ @@ -1036,7 +1036,8 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) /* If the lsa's age reached to MaxAge, start flushing procedure. */ if (IS_LSA_MAXAGE (lsa)) { - lp->flags &= ~LPFLG_LSA_ENGAGED; + if (lp) + lp->flags &= ~LPFLG_LSA_ENGAGED; ospf_opaque_lsa_flush_schedule (lsa); goto out; } diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h index e8511cdf..863d8ba8 100644 --- a/ospfd/ospf_te.h +++ b/ospfd/ospf_te.h @@ -41,7 +41,7 @@ * */ -#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) +#define MAX_LEGAL_TE_INSTANCE_NUM (0xffff) /* * 24 16 8 0 diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 97c8e8d6..72493a20 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" - + static const char *ospf_network_type_str[] = { "Null", @@ -61,7 +61,7 @@ static const char *ospf_network_type_str[] = "LOOPBACK" }; - + /* Utility functions. */ static int ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) @@ -94,7 +94,7 @@ ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) return 0; } - + static int str2metric (const char *str, int *metric) { @@ -142,7 +142,7 @@ ospf_oi_count (struct interface *ifp) return i; } - + DEFUN (router_ospf, router_ospf_cmd, "router ospf", @@ -490,7 +490,7 @@ DEFUN (no_ospf_network_area, return CMD_SUCCESS; } - + DEFUN (ospf_area_range, ospf_area_range_cmd, "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M", @@ -634,7 +634,7 @@ ALIAS (no_ospf_area_range, "Advertise this range (default)\n" "User specified metric for this range\n" "Advertised metric for this range\n") - + DEFUN (ospf_area_range_substitute, ospf_area_range_substitute_cmd, "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M substitute A.B.C.D/M", @@ -686,7 +686,7 @@ DEFUN (no_ospf_area_range_substitute, return CMD_SUCCESS; } - + /* Command Handler Logic in VLink stuff is delicate!! ALTER AT YOUR OWN RISK!!!! @@ -771,7 +771,7 @@ ospf_find_vl_data (struct ospf *ospf, struct ospf_vl_config_data *vl_config) { vl_data->vl_oi = ospf_vl_new (ospf, vl_data); ospf_vl_add (ospf, vl_data); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } } return vl_data; @@ -1382,7 +1382,7 @@ ALIAS (no_ospf_area_vlink, VLINK_HELPSTR_AUTHTYPE_SIMPLE VLINK_HELPSTR_AUTH_MD5) - + DEFUN (ospf_area_shortcut, ospf_area_shortcut_cmd, "area (A.B.C.D|<0-4294967295>) shortcut (default|enable|disable)", @@ -1450,7 +1450,7 @@ DEFUN (no_ospf_area_shortcut, return CMD_SUCCESS; } - + DEFUN (ospf_area_stub, ospf_area_stub_cmd, "area (A.B.C.D|<0-4294967295>) stub", @@ -1742,12 +1742,11 @@ DEFUN (no_ospf_area_default_cost, struct ospf *ospf = vty->index; struct ospf_area *area; struct in_addr area_id; - u_int32_t cost; int format; struct prefix_ipv4 p; VTY_GET_OSPF_AREA_ID_NO_BB ("default-cost", area_id, format, argv[0]); - VTY_GET_INTEGER_RANGE ("stub default cost", cost, argv[1], 0, 16777215); + VTY_CHECK_INTEGER_RANGE ("stub default cost", argv[1], 0, OSPF_LS_INFINITY); area = ospf_area_lookup_by_area_id (ospf, area_id); if (area == NULL) @@ -1933,7 +1932,6 @@ DEFUN (no_ospf_area_filter_list, struct ospf *ospf = vty->index; struct ospf_area *area; struct in_addr area_id; - struct prefix_list *plist; int format; VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); @@ -1941,7 +1939,6 @@ DEFUN (no_ospf_area_filter_list, if ((area = ospf_area_lookup_by_area_id (ospf, area_id)) == NULL) return CMD_SUCCESS; - plist = prefix_list_lookup (AFI_IP, argv[1]); if (strncmp (argv[2], "in", 2) == 0) { if (PREFIX_NAME_IN (area)) @@ -1974,11 +1971,13 @@ DEFUN (no_ospf_area_filter_list, return CMD_SUCCESS; } - + DEFUN (ospf_area_authentication_message_digest, ospf_area_authentication_message_digest_cmd, "area (A.B.C.D|<0-4294967295>) authentication message-digest", "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" "Enable authentication\n" "Use message-digest authentication\n") { @@ -2043,7 +2042,7 @@ DEFUN (no_ospf_area_authentication, return CMD_SUCCESS; } - + DEFUN (ospf_abr_type, ospf_abr_type_cmd, "ospf abr-type (cisco|ibm|shortcut|standard)", @@ -2173,7 +2172,7 @@ DEFUN (ospf_compatible_rfc1583, if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) { SET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } return CMD_SUCCESS; } @@ -2190,7 +2189,7 @@ DEFUN (no_ospf_compatible_rfc1583, if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) { UNSET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } return CMD_SUCCESS; } @@ -2207,7 +2206,7 @@ ALIAS (no_ospf_compatible_rfc1583, NO_STR "OSPF specific commands\n" "Disable the RFC1583Compatibility flag\n") - + static int ospf_timers_spf_set (struct vty *vty, unsigned int delay, unsigned int hold, @@ -2298,7 +2297,7 @@ ALIAS_DEPRECATED (no_ospf_timers_throttle_spf, NO_STR "Adjust routing timers\n" "OSPF SPF timers\n") - + DEFUN (ospf_neighbor, ospf_neighbor_cmd, "neighbor A.B.C.D", @@ -2322,7 +2321,7 @@ DEFUN (ospf_neighbor, if (argc > 1) ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority); if (argc > 2) - ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, priority); + ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval); return CMD_SUCCESS; } @@ -2394,11 +2393,10 @@ DEFUN (no_ospf_neighbor, { struct ospf *ospf = vty->index; struct in_addr nbr_addr; - int ret; VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]); - ret = ospf_nbr_nbma_unset (ospf, nbr_addr); + (void)ospf_nbr_nbma_unset (ospf, nbr_addr); return CMD_SUCCESS; } @@ -2432,7 +2430,7 @@ ALIAS (no_ospf_neighbor, "Dead Neighbor Polling interval\n" "Seconds\n") - + DEFUN (ospf_refresh_timer, ospf_refresh_timer_cmd, "refresh timer <10-1800>", "Adjust refresh parameters\n" @@ -2550,7 +2548,7 @@ const char *ospf_shortcut_mode_descr_str[] = }; - + static void show_ip_ospf_area (struct vty *vty, struct ospf_area *area) { @@ -2751,6 +2749,9 @@ DEFUN (show_ip_ospf, vty_out (vty, "last executed %s ago%s", ospf_timeval_dump (&result, timebuf, sizeof (timebuf)), VTY_NEWLINE); + vty_out (vty, " Last SPF duration %s%s", + ospf_timeval_dump (&ospf->ts_spf_duration, timebuf, sizeof (timebuf)), + VTY_NEWLINE); } else vty_out (vty, "has not been run%s", VTY_NEWLINE); @@ -2802,7 +2803,7 @@ DEFUN (show_ip_ospf, return CMD_SUCCESS; } - + static void show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, struct interface *ifp) @@ -3431,7 +3432,7 @@ DEFUN (show_ip_ospf_neighbor_int_detail, return CMD_SUCCESS; } - + /* Show functions */ static int show_lsa_summary (struct vty *vty, struct ospf_lsa *lsa, int self) @@ -4035,21 +4036,25 @@ show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self) static void show_ip_ospf_database_maxage (struct vty *vty, struct ospf *ospf) { - struct listnode *node; - struct ospf_lsa *lsa; + struct route_node *rn; vty_out (vty, "%s MaxAge Link States:%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (ospf->maxage_lsa, node, lsa)) + for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) { - vty_out (vty, "Link type: %d%s", lsa->data->type, VTY_NEWLINE); - vty_out (vty, "Link State ID: %s%s", - inet_ntoa (lsa->data->id), VTY_NEWLINE); - vty_out (vty, "Advertising Router: %s%s", - inet_ntoa (lsa->data->adv_router), VTY_NEWLINE); - vty_out (vty, "LSA lock count: %d%s", lsa->lock, VTY_NEWLINE); - vty_out (vty, "%s", VTY_NEWLINE); + struct ospf_lsa *lsa; + + if ((lsa = rn->info) != NULL) + { + vty_out (vty, "Link type: %d%s", lsa->data->type, VTY_NEWLINE); + vty_out (vty, "Link State ID: %s%s", + inet_ntoa (lsa->data->id), VTY_NEWLINE); + vty_out (vty, "Advertising Router: %s%s", + inet_ntoa (lsa->data->adv_router), VTY_NEWLINE); + vty_out (vty, "LSA lock count: %d%s", lsa->lock, VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } } } @@ -4299,7 +4304,7 @@ ALIAS (show_ip_ospf_database_type_adv_router, OSPF_LSA_TYPES_DESC "Self-originated link states\n") - + DEFUN (ip_ospf_authentication_args, ip_ospf_authentication_args_addr_cmd, "ip ospf authentication (null|message-digest) A.B.C.D", @@ -5306,7 +5311,13 @@ DEFUN (ip_ospf_network, struct interface *ifp = vty->index; int old_type = IF_DEF_PARAMS (ifp)->type; struct route_node *rn; - + + if (old_type == OSPF_IFTYPE_LOOPBACK) + { + vty_out (vty, "This is a loopback interface. Can't set network type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strncmp (argv[0], "b", 1) == 0) IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_BROADCAST; else if (strncmp (argv[0], "n", 1) == 0) @@ -5403,7 +5414,7 @@ DEFUN (ip_ospf_priority, "Address of interface") { struct interface *ifp = vty->index; - u_int32_t priority; + long priority; struct route_node *rn; struct in_addr addr; int ret; @@ -5784,11 +5795,10 @@ ALIAS (no_ip_ospf_transmit_delay, "OSPF interface commands\n" "Link state transmit delay\n") - -DEFUN (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_type_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> metric-type (1|2) route-map WORD", +DEFUN (ospf_redistribute_source, + ospf_redistribute_source_cmd, + "redistribute " QUAGGA_REDIST_STR_OSPFD + " {metric <0-16777214>|metric-type (1|2)|route-map WORD}", REDIST_STR QUAGGA_REDIST_HELP_STR_OSPFD "Metric for redistributed routes\n" @@ -5804,84 +5814,25 @@ DEFUN (ospf_redistribute_source_metric_type, int type = -1; int metric = -1; + if (argc < 4) + return CMD_WARNING; /* should not happen */ + /* Get distribute source. */ source = proto_redistnum(AFI_IP, argv[0]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; /* Get metric value. */ - if (argc >= 2) + if (argv[1] != NULL) if (!str2metric (argv[1], &metric)) return CMD_WARNING; /* Get metric type. */ - if (argc >= 3) + if (argv[2] != NULL) if (!str2metric_type (argv[2], &type)) return CMD_WARNING; - if (argc == 4) - ospf_routemap_set (ospf, source, argv[3]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, type, metric); -} - -ALIAS (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_type_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> metric-type (1|2)", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n" - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " metric <0-16777214>", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n") - -DEFUN (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_metric_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) metric <0-16777214> route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Metric for redistributed routes\n" - "OSPF default metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int type = -1; - int metric = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - /* Get metric type. */ - if (argc >= 3) - if (!str2metric (argv[2], &metric)) - return CMD_WARNING; - - if (argc == 4) + if (argv[3] != NULL) ospf_routemap_set (ospf, source, argv[3]); else ospf_routemap_unset (ospf, source); @@ -5889,124 +5840,6 @@ DEFUN (ospf_redistribute_source_type_metric, return ospf_redistribute_set (ospf, source, type, metric); } -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_metric_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) metric <0-16777214>", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Metric for redistributed routes\n" - "OSPF default metric\n") - -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " metric-type (1|2)", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD, - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD) - -DEFUN (ospf_redistribute_source_metric_routemap, - ospf_redistribute_source_metric_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int metric = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric (argv[1], &metric)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, source, argv[2]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, -1, metric); -} - -DEFUN (ospf_redistribute_source_type_routemap, - ospf_redistribute_source_type_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int type = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, source, argv[2]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, type, -1); -} - -DEFUN (ospf_redistribute_source_routemap, - ospf_redistribute_source_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, source, argv[1]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, -1, -1); -} - DEFUN (no_ospf_redistribute_source, no_ospf_redistribute_source_cmd, "no redistribute " QUAGGA_REDIST_STR_OSPFD, @@ -6037,7 +5870,7 @@ DEFUN (ospf_distribute_list_out, int source; /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); + source = proto_redistnum(AFI_IP, argv[1]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; @@ -6056,7 +5889,7 @@ DEFUN (no_ospf_distribute_list_out, struct ospf *ospf = vty->index; int source; - source = proto_redistnum(AFI_IP, argv[0]); + source = proto_redistnum(AFI_IP, argv[1]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; @@ -6064,11 +5897,13 @@ DEFUN (no_ospf_distribute_list_out, } /* Default information originate. */ -DEFUN (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_type_routemap_cmd, - "default-information originate metric <0-16777214> metric-type (1|2) route-map WORD", +DEFUN (ospf_default_information_originate, + ospf_default_information_originate_cmd, + "default-information originate " + "{always|metric <0-16777214>|metric-type (1|2)|route-map WORD}", "Control distribution of default information\n" "Distribute a default route\n" + "Always advertise default route\n" "OSPF default metric\n" "OSPF metric\n" "OSPF metric type for default routes\n" @@ -6078,439 +5913,90 @@ DEFUN (ospf_default_information_originate_metric_type_routemap, "Pointer to route-map entries\n") { struct ospf *ospf = vty->index; + int default_originate = DEFAULT_ORIGINATE_ZEBRA; int type = -1; int metric = -1; + if (argc < 4) + return CMD_WARNING; /* this should not happen */ + + /* Check whether "always" was specified */ + if (argv[0] != NULL) + default_originate = DEFAULT_ORIGINATE_ALWAYS; + /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) + if (argv[1] != NULL) + if (!str2metric (argv[1], &metric)) return CMD_WARNING; /* Get metric type. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) + if (argv[2] != NULL) + if (!str2metric_type (argv[2], &type)) return CMD_WARNING; - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); + if (argv[3] != NULL) + ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[3]); else ospf_routemap_unset (ospf, DEFAULT_ROUTE); - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, + return ospf_redistribute_default_set (ospf, default_originate, type, metric); } -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_type_cmd, - "default-information originate metric <0-16777214> metric-type (1|2)", +DEFUN (no_ospf_default_information_originate, + no_ospf_default_information_originate_cmd, + "no default-information originate", + NO_STR "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") + "Distribute a default route\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix.s_addr = 0; + p.prefixlen = 0; -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_cmd, - "default-information originate metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n") + ospf_external_lsa_flush (ospf, DEFAULT_ROUTE, &p, 0); -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_cmd, - "default-information originate", - "Control distribution of default information\n" - "Distribute a default route\n") + if (EXTERNAL_INFO (DEFAULT_ROUTE)) { + ospf_external_info_delete (DEFAULT_ROUTE, p); + route_table_finish (EXTERNAL_INFO (DEFAULT_ROUTE)); + EXTERNAL_INFO (DEFAULT_ROUTE) = NULL; + } -/* Default information originate. */ -DEFUN (ospf_default_information_originate_metric_routemap, - ospf_default_information_originate_metric_routemap_cmd, - "default-information originate metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") + ospf_routemap_unset (ospf, DEFAULT_ROUTE); + return ospf_redistribute_default_unset (ospf); +} + +DEFUN (ospf_default_metric, + ospf_default_metric_cmd, + "default-metric <0-16777214>", + "Set metric of redistributed routes\n" + "Default metric\n") { struct ospf *ospf = vty->index; int metric = -1; - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; + if (!str2metric (argv[0], &metric)) + return CMD_WARNING; - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); + ospf->default_metric = metric; - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - -1, metric); + return CMD_SUCCESS; } -/* Default information originate. */ -DEFUN (ospf_default_information_originate_routemap, - ospf_default_information_originate_routemap_cmd, - "default-information originate route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Route map reference\n" - "Pointer to route-map entries\n") +DEFUN (no_ospf_default_metric, + no_ospf_default_metric_cmd, + "no default-metric", + NO_STR + "Set metric of redistributed routes\n") { struct ospf *ospf = vty->index; - if (argc == 1) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[0]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); + ospf->default_metric = -1; - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, -1, -1); -} - -DEFUN (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_metric_routemap_cmd, - "default-information originate metric-type (1|2) metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric (argv[1], &metric)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - type, metric); -} - -ALIAS (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_metric_cmd, - "default-information originate metric-type (1|2) metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n") - -ALIAS (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_cmd, - "default-information originate metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -DEFUN (ospf_default_information_originate_type_routemap, - ospf_default_information_originate_type_routemap_cmd, - "default-information originate metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - type, -1); -} - -DEFUN (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_type_routemap_cmd, - "default-information originate always metric <0-16777214> metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - /* Get metric type. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, metric); -} - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_type_cmd, - "default-information originate always metric <0-16777214> metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_cmd, - "default-information originate always metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n") - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_cmd, - "default-information originate always", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n") - -DEFUN (ospf_default_information_originate_always_metric_routemap, - ospf_default_information_originate_always_metric_routemap_cmd, - "default-information originate always metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - -1, metric); -} - -DEFUN (ospf_default_information_originate_always_routemap, - ospf_default_information_originate_always_routemap_cmd, - "default-information originate always route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - - if (argc == 1) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[0]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, -1, -1); -} - -DEFUN (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_metric_routemap_cmd, - "default-information originate always metric-type (1|2) metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric (argv[1], &metric)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, metric); -} - -ALIAS (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_metric_cmd, - "default-information originate always metric-type (1|2) metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n") - -ALIAS (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_cmd, - "default-information originate always metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -DEFUN (ospf_default_information_originate_always_type_routemap, - ospf_default_information_originate_always_type_routemap_cmd, - "default-information originate always metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, -1); -} - -DEFUN (no_ospf_default_information_originate, - no_ospf_default_information_originate_cmd, - "no default-information originate", - NO_STR - "Control distribution of default information\n" - "Distribute a default route\n") -{ - struct ospf *ospf = vty->index; - struct prefix_ipv4 p; - - p.family = AF_INET; - p.prefix.s_addr = 0; - p.prefixlen = 0; - - ospf_external_lsa_flush (ospf, DEFAULT_ROUTE, &p, 0); - - if (EXTERNAL_INFO (DEFAULT_ROUTE)) { - ospf_external_info_delete (DEFAULT_ROUTE, p); - route_table_finish (EXTERNAL_INFO (DEFAULT_ROUTE)); - EXTERNAL_INFO (DEFAULT_ROUTE) = NULL; - } - - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - return ospf_redistribute_default_unset (ospf); -} - -DEFUN (ospf_default_metric, - ospf_default_metric_cmd, - "default-metric <0-16777214>", - "Set metric of redistributed routes\n" - "Default metric\n") -{ - struct ospf *ospf = vty->index; - int metric = -1; - - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - ospf->default_metric = metric; - - return CMD_SUCCESS; -} - -DEFUN (no_ospf_default_metric, - no_ospf_default_metric_cmd, - "no default-metric", - NO_STR - "Set metric of redistributed routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->default_metric = -1; - - return CMD_SUCCESS; + return CMD_SUCCESS; } ALIAS (no_ospf_default_metric, @@ -6549,296 +6035,73 @@ DEFUN (no_ospf_distance, DEFUN (no_ospf_distance_ospf, no_ospf_distance_ospf_cmd, - "no distance ospf", + "no distance ospf {intra-area|inter-area|external}", NO_STR "Define an administrative distance\n" "OSPF Administrative distance\n" - "OSPF Distance\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = 0; - ospf->distance_inter = 0; - ospf->distance_external = 0; - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra, - ospf_distance_ospf_intra_cmd, - "distance ospf intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_inter, - ospf_distance_ospf_intra_inter_cmd, - "distance ospf intra-area <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_external, - ospf_distance_ospf_intra_external_cmd, - "distance ospf intra-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_inter_external, - ospf_distance_ospf_intra_inter_external_cmd, - "distance ospf intra-area <1-255> inter-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" + "OSPF Distance\n" "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - ospf->distance_external = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_external_inter, - ospf_distance_ospf_intra_external_inter_cmd, - "distance ospf intra-area <1-255> external <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - ospf->distance_inter = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_inter, - ospf_distance_ospf_inter_cmd, - "distance ospf inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" "Inter-area routes\n" - "Distance for inter-area routes\n") + "External routes\n") { struct ospf *ospf = vty->index; - ospf->distance_inter = atoi (argv[0]); - - return CMD_SUCCESS; -} + if (argc < 3) + return CMD_WARNING; -DEFUN (ospf_distance_ospf_inter_intra, - ospf_distance_ospf_inter_intra_cmd, - "distance ospf inter-area <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[0] != NULL) + ospf->distance_intra = 0; - ospf->distance_inter = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); + if (argv[1] != NULL) + ospf->distance_inter = 0; - return CMD_SUCCESS; -} + if (argv[2] != NULL) + ospf->distance_external = 0; -DEFUN (ospf_distance_ospf_inter_external, - ospf_distance_ospf_inter_external_cmd, - "distance ospf inter-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[0] || argv[1] || argv[2]) + return CMD_SUCCESS; - ospf->distance_inter = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); + /* If no arguments are given, clear all distance information */ + ospf->distance_intra = 0; + ospf->distance_inter = 0; + ospf->distance_external = 0; return CMD_SUCCESS; } -DEFUN (ospf_distance_ospf_inter_intra_external, - ospf_distance_ospf_inter_intra_external_cmd, - "distance ospf inter-area <1-255> intra-area <1-255> external <1-255>", +DEFUN (ospf_distance_ospf, + ospf_distance_ospf_cmd, + "distance ospf " + "{intra-area <1-255>|inter-area <1-255>|external <1-255>}", "Define an administrative distance\n" "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" "Intra-area routes\n" "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_inter = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - ospf->distance_external = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_inter_external_intra, - ospf_distance_ospf_inter_external_intra_cmd, - "distance ospf inter-area <1-255> external <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" "Inter-area routes\n" "Distance for inter-area routes\n" "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_inter = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - ospf->distance_intra = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external, - ospf_distance_ospf_external_cmd, - "distance ospf external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" "Distance for external routes\n") { struct ospf *ospf = vty->index; - ospf->distance_external = atoi (argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_intra, - ospf_distance_ospf_external_intra_cmd, - "distance ospf external <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_external = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_inter, - ospf_distance_ospf_external_inter_cmd, - "distance ospf external <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_external = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_intra_inter, - ospf_distance_ospf_external_intra_inter_cmd, - "distance ospf external <1-255> intra-area <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argc < 3) /* should not happen */ + return CMD_WARNING; - ospf->distance_external = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - ospf->distance_inter = atoi (argv[2]); + if (!argv[0] && !argv[1] && !argv[2]) + { + vty_out(vty, "%% Command incomplete. (Arguments required)%s", + VTY_NEWLINE); + return CMD_WARNING; + } - return CMD_SUCCESS; -} + if (argv[0] != NULL) + ospf->distance_intra = atoi(argv[0]); -DEFUN (ospf_distance_ospf_external_inter_intra, - ospf_distance_ospf_external_inter_intra_cmd, - "distance ospf external <1-255> inter-area <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[1] != NULL) + ospf->distance_inter = atoi(argv[1]); - ospf->distance_external = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - ospf->distance_intra = atoi (argv[2]); + if (argv[2] != NULL) + ospf->distance_external = atoi(argv[2]); return CMD_SUCCESS; } @@ -7001,7 +6264,7 @@ ALIAS (no_ip_ospf_mtu_ignore, "IP Information\n" "OSPF interface commands\n" "Disable mtu mismatch detection\n") - + DEFUN (ospf_max_metric_router_lsa_admin, ospf_max_metric_router_lsa_admin_cmd, "max-metric router-lsa administrative", @@ -7020,6 +6283,10 @@ DEFUN (ospf_max_metric_router_lsa_admin, if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) ospf_router_lsa_update_area (area); } + + /* Allows for areas configured later to get the property */ + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_SET; + return CMD_SUCCESS; } @@ -7047,6 +6314,7 @@ DEFUN (no_ospf_max_metric_router_lsa_admin, ospf_router_lsa_update_area (area); } } + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; return CMD_SUCCESS; } @@ -7165,7 +6433,7 @@ config_write_stub_router (struct vty *vty, struct ospf *ospf) } return; } - + static void show_ip_ospf_route_network (struct vty *vty, struct route_table *rt) { @@ -7384,7 +6652,7 @@ DEFUN (show_ip_ospf_route, return CMD_SUCCESS; } - + const char *ospf_abr_type_str[] = { "unknown", @@ -7413,7 +6681,7 @@ area_id2str (char *buf, int length, struct ospf_area *area) sprintf (buf, "%lu", (unsigned long) ntohl (area->area_id.s_addr)); } - + const char *ospf_int_type_str[] = { "unknown", /* should never be used. */ @@ -7847,7 +7115,7 @@ config_write_virtual_link (struct vty *vty, struct ospf *ospf) return 0; } - + static int config_write_ospf_redistribute (struct vty *vty, struct ospf *ospf) { @@ -8140,7 +7408,7 @@ ospf_vty_show_init (void) install_element (ENABLE_NODE, &show_ip_ospf_border_routers_cmd); } - + /* ospfd's interface node. */ static struct cmd_node interface_node = { @@ -8260,63 +7528,13 @@ ospf_vty_if_init (void) static void ospf_vty_zebra_init (void) { - install_element (OSPF_NODE, &ospf_redistribute_source_type_metric_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_type_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_type_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_cmd); install_element (OSPF_NODE, &ospf_redistribute_source_cmd); - install_element (OSPF_NODE, - &ospf_redistribute_source_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_redistribute_source_type_metric_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_type_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_routemap_cmd); - install_element (OSPF_NODE, &no_ospf_redistribute_source_cmd); install_element (OSPF_NODE, &ospf_distribute_list_out_cmd); install_element (OSPF_NODE, &no_ospf_distribute_list_out_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_type_cmd); - install_element (OSPF_NODE, &ospf_default_information_originate_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_metric_cmd); - install_element (OSPF_NODE, &ospf_default_information_originate_type_cmd); install_element (OSPF_NODE, &ospf_default_information_originate_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_type_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_cmd); - - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_routemap_cmd); - install_element (OSPF_NODE, &no_ospf_default_information_originate_cmd); install_element (OSPF_NODE, &ospf_default_metric_cmd); @@ -8326,21 +7544,7 @@ ospf_vty_zebra_init (void) install_element (OSPF_NODE, &ospf_distance_cmd); install_element (OSPF_NODE, &no_ospf_distance_cmd); install_element (OSPF_NODE, &no_ospf_distance_ospf_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_inter_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_external_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_intra_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_external_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_intra_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_inter_intra_cmd); + install_element (OSPF_NODE, &ospf_distance_ospf_cmd); #if 0 install_element (OSPF_NODE, &ospf_distance_source_cmd); install_element (OSPF_NODE, &no_ospf_distance_source_cmd); @@ -8356,7 +7560,7 @@ static struct cmd_node ospf_node = 1 }; - + /* Install OSPF related vty commands. */ void ospf_vty_init (void) diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index f5f49f64..b6d3260b 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -133,8 +133,8 @@ ospf_interface_delete (int command, struct zclient *zclient, if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) zlog_debug - ("Zebra: interface delete %s index %d flags %lld metric %d mtu %d", - ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); + ("Zebra: interface delete %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); #ifdef HAVE_SNMP ospf_snmp_if_delete (ifp); @@ -482,7 +482,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra: Route add %s/%d nexthop %s", + zlog_debug("Zebra: Route delete %s/%d nexthop %s", inet_ntop(AF_INET, &p->prefix, buf[0], sizeof(buf[0])), p->prefixlen, @@ -918,7 +918,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, return 0; } - + int ospf_distribute_list_out_set (struct ospf *ospf, int type, const char *name) @@ -1004,7 +1004,7 @@ ospf_distribute_list_update_timer (struct thread *thread) /* Update distribute-list and set timer to apply access-list. */ void -ospf_distribute_list_update (struct ospf *ospf, int type) +ospf_distribute_list_update (struct ospf *ospf, uintptr_t type) { struct route_table *rt; @@ -1217,7 +1217,6 @@ ospf_distance_unset (struct vty *vty, struct ospf *ospf, { int ret; struct prefix_ipv4 p; - u_char distance; struct route_node *rn; struct ospf_distance *odistance; @@ -1228,8 +1227,6 @@ ospf_distance_unset (struct vty *vty, struct ospf *ospf, return CMD_WARNING; } - distance = atoi (distance_str); - rn = route_node_lookup (ospf->distance_table, (struct prefix *) &p); if (!rn) { diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index fbb34442..148f6520 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -54,7 +54,7 @@ extern int ospf_redistribute_check (struct ospf *, struct external_info *, int *); extern int ospf_distribute_check_connected (struct ospf *, struct external_info *); -extern void ospf_distribute_list_update (struct ospf *, int); +extern void ospf_distribute_list_update (struct ospf *, uintptr_t); extern int ospf_is_type_redistributed (int); extern void ospf_distance_reset (struct ospf *); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index e8405136..c55bdaeb 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -53,7 +53,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "ospfd/ospf_route.h" #include "ospfd/ospf_ase.h" - + /* OSPF process wide configuration. */ static struct ospf_master ospf_master; @@ -64,7 +64,7 @@ struct ospf_master *om; extern struct zclient *zclient; extern struct in_addr router_id_zebra; - + static void ospf_remove_vls_through_area (struct ospf *, struct ospf_area *); static void ospf_network_free (struct ospf *, struct ospf_network *); static void ospf_area_free (struct ospf_area *); @@ -76,7 +76,7 @@ static int ospf_network_match_iface (const struct connected *, static void ospf_finish_final (struct ospf *); #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1 - + void ospf_router_id_update (struct ospf *ospf) { @@ -139,7 +139,7 @@ ospf_router_id_update (struct ospf *ospf) ospf_if_update (ospf, ifp); } } - + /* For OSPF area sort by area id. */ static int ospf_area_id_cmp (struct ospf_area *a1, struct ospf_area *a2) @@ -182,7 +182,8 @@ ospf_new (void) new->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED; new->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; - + new->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; + /* Distribute parameter init. */ for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { @@ -199,8 +200,8 @@ ospf_new (void) new->spf_hold_multiplier = 1; /* MaxAge init. */ - new->maxage_delay = OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; - new->maxage_lsa = list_new (); + new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; + new->maxage_lsa = route_table_init(); new->t_maxage_walker = thread_add_timer (master, ospf_lsa_maxage_walker, new, OSPF_LSA_MAXAGE_CHECK_INTERVAL); @@ -222,7 +223,7 @@ ospf_new (void) } new->maxsndbuflen = getsockopt_so_sendbuf (new->fd); if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) - zlog_debug ("%s: starting with OSPF send buffer size %d", + zlog_debug ("%s: starting with OSPF send buffer size %u", __func__, new->maxsndbuflen); if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE+1)) == NULL) { @@ -278,7 +279,7 @@ ospf_get () return ospf; } - + /* Handle the second half of deferred shutdown. This is called either * from the deferred-shutdown timer thread, or directly through * ospf_deferred_shutdown_check. @@ -353,7 +354,7 @@ ospf_deferred_shutdown_check (struct ospf *ospf) timeout); return; } - + /* Shut down the entire process */ void ospf_terminate (void) @@ -501,10 +502,18 @@ ospf_finish_final (struct ospf *ospf) ospf_lsdb_delete_all (ospf->lsdb); ospf_lsdb_free (ospf->lsdb); - for (ALL_LIST_ELEMENTS (ospf->maxage_lsa, node, nnode, lsa)) - ospf_lsa_unlock (&lsa); /* maxage_lsa */ + for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) + { + struct ospf_lsa *lsa; - list_delete (ospf->maxage_lsa); + if ((lsa = rn->info) != NULL) + { + ospf_lsa_unlock (&lsa); + rn->info = NULL; + } + route_unlock_node (rn); + } + route_table_finish (ospf->maxage_lsa); if (ospf->old_table) ospf_route_table_free (ospf->old_table); @@ -554,7 +563,7 @@ ospf_finish_final (struct ospf *ospf) XFREE (MTYPE_OSPF_TOP, ospf); } - + /* allocate new OSPF Area object */ static struct ospf_area * ospf_area_new (struct ospf *ospf, struct in_addr area_id) @@ -676,6 +685,10 @@ ospf_area_get (struct ospf *ospf, struct in_addr area_id, int format) area->format = format; listnode_add_sort (ospf->areas, area); ospf_check_abr_status (ospf); + if (ospf->stub_router_admin_set == OSPF_STUB_ROUTER_ADMINISTRATIVE_SET) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + } } return area; @@ -706,7 +719,7 @@ ospf_area_del_if (struct ospf_area *area, struct ospf_interface *oi) listnode_delete (area->oiflist, oi); } - + /* Config network statement related functions. */ static struct ospf_network * ospf_network_new (struct in_addr area_id, int format) @@ -814,7 +827,10 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p, } if (found == 0) - ospf_if_free (oi); + { + ospf_if_free (oi); + ospf_area_check_free (ospf, area_id); + } } /* Update connected redistribute. */ @@ -974,7 +990,7 @@ ospf_remove_vls_through_area (struct ospf *ospf, struct ospf_area *area) ospf_vl_delete (ospf, vl_data); } - + static const struct message ospf_area_type_msg[] = { { OSPF_AREA_DEFAULT, "Default" }, @@ -1337,7 +1353,7 @@ ospf_timers_refresh_unset (struct ospf *ospf) return 1; } - + static struct ospf_nbr_nbma * ospf_nbr_nbma_new (void) { diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index bf825d17..bf70d022 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -24,22 +24,13 @@ #define _ZEBRA_OSPFD_H #include +#include "libospf.h" #include "filter.h" #include "log.h" #define OSPF_VERSION 2 -/* Default protocol, port number. */ -#ifndef IPPROTO_OSPFIGP -#define IPPROTO_OSPFIGP 89 -#endif /* IPPROTO_OSPFIGP */ - -/* IP precedence. */ -#ifndef IPTOS_PREC_INTERNETCONTROL -#define IPTOS_PREC_INTERNETCONTROL 0xC0 -#endif /* IPTOS_PREC_INTERNETCONTROL */ - /* VTY port number. */ #define OSPF_VTY_PORT 2604 @@ -50,29 +41,11 @@ /* Default configuration file name for ospfd. */ #define OSPF_DEFAULT_CONFIG "ospfd.conf" -/* Architectual Constants */ -#ifdef DEBUG -#define OSPF_LS_REFRESH_TIME 60 -#else -#define OSPF_LS_REFRESH_TIME 1800 -#endif -#define OSPF_MIN_LS_INTERVAL 5 -#define OSPF_MIN_LS_ARRIVAL 1 -#define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ -#define OSPF_LSA_MAXAGE 3600 -#define OSPF_CHECK_AGE 300 -#define OSPF_LSA_MAXAGE_DIFF 900 -#define OSPF_LS_INFINITY 0xffffff -#define OSPF_DEFAULT_DESTINATION 0x00000000 /* 0.0.0.0 */ -#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001 -#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffff - #define OSPF_NSSA_TRANS_STABLE_DEFAULT 40 #define OSPF_ALLSPFROUTERS 0xe0000005 /* 224.0.0.5 */ #define OSPF_ALLDROUTERS 0xe0000006 /* 224.0.0.6 */ -#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ /* OSPF Authentication Type. */ #define OSPF_AUTH_NULL 0 @@ -85,30 +58,6 @@ been given or not in VLink command handlers */ #define OSPF_AUTH_CMD_NOTSEEN -2 -/* OSPF SPF timer values. */ -#define OSPF_SPF_DELAY_DEFAULT 200 -#define OSPF_SPF_HOLDTIME_DEFAULT 1000 -#define OSPF_SPF_MAX_HOLDTIME_DEFAULT 10000 - -/* OSPF interface default values. */ -#define OSPF_OUTPUT_COST_DEFAULT 10 -#define OSPF_OUTPUT_COST_INFINITE UINT16_MAX -#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 -#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 -#define OSPF_HELLO_INTERVAL_DEFAULT 10 -#define OSPF_ROUTER_PRIORITY_DEFAULT 1 -#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 -#define OSPF_TRANSMIT_DELAY_DEFAULT 1 -#define OSPF_DEFAULT_BANDWIDTH 10000 /* Kbps */ - -#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */ - -#define OSPF_POLL_INTERVAL_DEFAULT 60 -#define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0 - -#define OSPF_MTU_IGNORE_DEFAULT 0 -#define OSPF_FAST_HELLO_DEFAULT 0 - /* OSPF options. */ #define OSPF_OPTION_T 0x01 /* TOS. */ #define OSPF_OPTION_E 0x02 @@ -195,6 +144,9 @@ struct ospf unsigned int stub_router_startup_time; /* seconds */ unsigned int stub_router_shutdown_time; /* seconds */ #define OSPF_STUB_ROUTER_UNCONFIGURED 0 + u_char stub_router_admin_set; +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_SET 1 +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET 0 #define OSPF_STUB_MAX_METRIC_SUMMARY_COST 0x00ff0000 @@ -242,10 +194,11 @@ struct ospf struct route_table *external_lsas; /* Database of external LSAs, prefix is LSA's adv. network*/ - /* Time stamps. */ + /* Time stamps */ struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ - struct list *maxage_lsa; /* List of MaxAge LSA for deletion. */ + struct route_table *maxage_lsa; /* List of MaxAge LSA for deletion. */ int redistribute; /* Num of redistributed protocols. */ /* Threads. */ @@ -259,10 +212,8 @@ struct ospf struct thread *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ #endif /* HAVE_OPAQUE_LSA */ -#define OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */ struct thread *t_maxage; /* MaxAge LSA remover timer. */ -#define OSPF_LSA_MAXAGE_CHECK_INTERVAL 30 struct thread *t_maxage_walker; /* MaxAge LSA checking timer. */ struct thread *t_deferred_shutdown; /* deferred/stub-router shutdown timer*/ @@ -270,7 +221,7 @@ struct ospf struct thread *t_write; struct thread *t_read; int fd; - int maxsndbuflen; + unsigned int maxsndbuflen; struct stream *ibuf; struct list *oi_write_q; @@ -443,6 +394,9 @@ struct ospf_area /* Statistics field. */ u_int32_t spf_calculation; /* SPF Calculation Count. */ + /* Time stamps. */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + /* Router count. */ u_int32_t abr_count; /* ABR router in this area. */ u_int32_t asbr_count; /* ASBR router in this area. */ diff --git a/pimd/.gitignore b/pimd/.gitignore new file mode 100644 index 00000000..51a2ac87 --- /dev/null +++ b/pimd/.gitignore @@ -0,0 +1,16 @@ +Makefile +Makefile.in +libpim.a +pimd +test_igmpv3_join +tags +TAGS +.deps +*.o +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/pimd/AUTHORS b/pimd/AUTHORS new file mode 100644 index 00000000..f6135a41 --- /dev/null +++ b/pimd/AUTHORS @@ -0,0 +1,9 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +# Everton da Silva Marques +$ more ~/.gitconfig +[user] + name = Everton Marques + email = everton.marques@gmail.com + +-x- diff --git a/pimd/CAVEATS b/pimd/CAVEATS new file mode 100644 index 00000000..9f07bda6 --- /dev/null +++ b/pimd/CAVEATS @@ -0,0 +1,178 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +C1 IGMPv3 backward compatibility with IGMPv1 and IGMPv2 is not + implemented. See RFC 3376, 7.3. Multicast Router Behavior. That's + because only Source-Specific Multicast is currently targeted. + +C2 IGMPv3 support for forwarding any-source groups is not + implemented. Traffic for groups in mode EXCLUDE {empty} won't be + forwarded. See RFC 3376, 6.3. Source-Specific Forwarding + Rules. That's because only Source-Specific Multicast is currently + targeted. + +C3 Load Splitting of IP Multicast Traffic over ECMP is not supported. + See also: RFC 2991 + Multipath Issues in Unicast and Multicast Next-Hop Selection + http://www.rfc-editor.org/rfc/rfc2991.txt + +C4 IPSec AH authentication is not supported (RFC 4601: + 6.3. Authentication Using IPsec). + +C5 PIM support is limited to SSM mode as defined in section 4.8.2 + (PIM-SSM-Only Routers) of RFC4601. That's because only + Source-Specific Multicast is currently targeted. + +C6 PIM implementation currently does not support IPv6. PIM-SSM + requires IGMPv3 for IPv4 and MLDv2 for IPv6. MLDv2 is currently + missing. See also CAVEAT C9. + +C7 FIXED (S,G) Assert state machine (RFC 4601, section 4.6.1) is not + implemented. See also TODO T6. See also CAVEAT C10. + +C8 It is not possible to disable join suppression in order to + explicitly track the join membership of individual downstream + routers. + - IGMPv3 Explicit Membership Tracking is not supported. + When explicit tracking is enabled on a router, the router can + individually track the Internet Group Management Protocol (IGMP) + membership state of all reporting hosts. This feature allows the + router to achieve minimal leave latencies when hosts leave a + multicast group or channel. Example: + conf t + interface eth0 + ip igmp explicit-tracking + +C9 Only IPv4 Address Family (number=1) is supported in the PIM Address + Family field. + See also RFC 4601: 5.1. PIM Address Family + See also CAVEAT C6. + See also http://www.iana.org/assignments/address-family-numbers + +C10 FIXED Assert metric depends on metric_preference and + route_metric. Those parameters should be fetched from RIB + (zebra). See also pim_rpf.c, pim_rpf_update(). + +C11 SSM Mapping is not supported + + SSM Mapping Overview: + + SSM mapping introduces a means for the last hop router to discover + sources sending to groups. When SSM mapping is configured, if a + router receives an IGMPv1 or IGMPv2 membership report for a + particular group G, the router translates this report into one or + more (S, G) channel memberships for the well-known sources + associated with this group. + + When the router receives an IGMPv1 or IGMPv2 membership report for + a group G, the router uses SSM mapping to determine one or more + source IP addresses for the group G. SSM mapping then translates + the membership report as an IGMPv3 report INCLUDE (G, [S1, G], + [S2, G]...[Sn, G] and continues as if it had received an IGMPv3 + report. The router then sends out PIM joins toward (S1, G) to (Sn, + G) and continues to be joined to these groups as long as it + continues to receive the IGMPv1 or IGMPv2 membership reports and + as long as the SSM mapping for the group remains the same. SSM + mapping, thus, enables you to leverage SSM for video delivery to + legacy STBs that do not support IGMPv3 or for applications that do + not take advantage of the IGMPv3 host stack. + + SSM mapping enables the last hop router to determine the source + addresses either by a statically configured table on the router or + by consulting a DNS server. When the statically configured table + is changed, or when the DNS mapping changes, the router will leave + the current sources associated with the joined groups. + +C12 FIXED MRIB for incongruent unicast/multicast topologies is not + supported. RPF mechanism currently just looks up the information + in the unicast routing table. + + See also: + RFC5110: 2.2.3. Issue: Overlapping Unicast/Multicast Topology + + Sometimes, multicast RPF mechanisms first look up the multicast + routing table, or M-RIB ("topology database") with a longest + prefix match algorithm, and if they find any entry (including a + default route), that is used; if no match is found, the unicast + routing table is used instead. + +C13 Can't detect change of primary address before the actual change. + Possible approach is to craft old interface address into ip source + address by using raw ip socket. + + See also: + + RFC 4601: 4.3.1. Sending Hello Messages + + Before an interface goes down or changes primary IP address, a + Hello message with a zero HoldTime should be sent immediately + (with the old IP address if the IP address changed). + + See also pim_sock_delete(). + +C14 FIXED Detection of interface primary address changes may fail when + there are multiple addresses. + See also TODO T32. + +C15 Changes in interface secondary address list are not immediately + detected. + See also detect_secondary_address_change + See also TODO T31. + +C16 AMT Draft (mboned-auto-multicast) is not supported. + AMT = Automatic IP Multicast Without Explicit Tunnels + + See also: + + Draft + http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast + http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast-09 + + AMT gateway implementation for Linux + http://cs.utdallas.edu/amt/ + + AMT for Streaming (IPTV) on Global IP Multicast by Greg Shepherd (Cisco) + http://nznog.miniconf.org/nznog-2008-sysadmin-miniconf-greg-shepherd-iptv.pdf + +C17 SNMP / RFC 5060 (PIM MIB) is not supported. + +C18 MFC never recovers from removal of static route to source + + # route add -host 1.2.3.4 gw 192.168.56.10 + Before removal: + quagga-pimd-router# sh ip mroute + Source Group Proto Input iVifI Output oVifI TTL Uptime + 1.2.3.4 232.1.2.3 I eth1 3 eth0 2 1 00:00:36 + + # route del -host 1.2.3.4 gw 192.168.56.10 + After removal: sh ip mroute --> empty output + + # route add -host 1.2.3.4 gw 192.168.56.10 + After the route is restored: sh ip mroute --> never recovers (empty output) + + At this point, "no ip pim ssm" on the upstream interface (eth0) crashes pimd: + + 2014/02/14 16:30:14 PIM: ifmembership_set: (S,G)=(1.2.3.4,232.1.2.3) membership now is NOINFO on interface eth0 + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_assert_tracking_desired: AssertTrackingDesired(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0 + 2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 2 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3) + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_could_assert: CouldAssert(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0 + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_my_assert_metric: my_assert_metric(1.2.3.4,232.1.2.3,eth0) changed from 0,0,0,10.0.2.15 to 1,4294967295,4294967295,0.0.0.0 + 2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 1 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3) + 2014/02/14 16:30:14 PIM: Assertion `!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)' failed in file pim_igmpv3.c, line 412, function igmp_source_delete + +C19 Provision to prevent group mode clash + + Beware group mode clash. A host/application issuing IGMPv2 + any-source joins for a group will disrupt SSM multicast for that + group. + + For instance, support for source-specific static igmp WILL FAIL if + there is host/application issuing IGMPv2 any-source joins for the + same group. + + The reason is the IGMPv2 any-source join forces qpimd to switch + the group mode to ASM (any-source multicast); however, qpimd is + unable to program ASM groups into the kernel; multicast won't + flow. There could be some provision to prevent such a behavior, + but currently there is none. + +-x- diff --git a/pimd/COMMANDS b/pimd/COMMANDS new file mode 100644 index 00000000..425ac822 --- /dev/null +++ b/pimd/COMMANDS @@ -0,0 +1,81 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +global configuration commands: + pimd: + ip multicast-routing Enable IP multicast forwarding + ip ssmpingd Enable ssmpingd operation + + zebra: + ip mroute Configure static unicast route into MRIB for multicast RPF lookup + +interface configuration commands: + pimd: + ip igmp Enable IGMP operation + ip igmp join IGMP join multicast group + ip igmp query-interval <1-1800> IGMP host query interval + ip igmp query-max-response-time <1-25> IGMP max query response (seconds) + ip igmp query-max-response-time-dsec <10-250> IGMP max query response (deciseconds) + ip pim ssm Enable PIM SSM operation + +verification commands: + pimd: + show ip igmp interface IGMP interface information + show ip igmp join IGMP static join information + show ip igmp parameters IGMP parameters information + show ip igmp groups IGMP groups information + show ip igmp groups retransmissions IGMP group retransmission + show ip igmp sources IGMP sources information + show ip igmp sources retransmissions IGMP source retransmission + show ip pim address PIM interface address + show ip pim assert PIM interface assert + show ip pim assert-internal PIM interface internal assert state + show ip pim assert-metric PIM interface assert metric + show ip pim assert-winner-metric PIM interface assert winner metric + show ip pim designated-router PIM interface designated router + show ip pim hello PIM interface hello information + show ip pim interface PIM interface information + show ip pim lan-prune-delay PIM neighbors LAN prune delay parameters + show ip pim local-membership PIM interface local-membership + show ip pim jp-override-interval PIM interface J/P override interval + show ip pim join PIM interface join information + show ip pim neighbor PIM neighbor information + show ip pim rpf PIM cached source rpf information + show ip pim secondary PIM neighbor addresses + show ip pim upstream PIM upstream information + show ip pim upstream-join-desired PIM upstream join-desired + show ip pim upstream-rpf PIM upstream source rpf + show ip multicast Multicast global information + show ip mroute IP multicast routing table + show ip mroute count Route and packet count data + show ip rib IP unicast routing table + show ip ssmpingd ssmpingd operation + + zebra: + show ip rpf Display RPF information for multicast source + +debug commands: + pimd: + clear ip interfaces Reset interfaces + clear ip igmp interfaces Reset IGMP interfaces + clear ip mroute Reset multicast routes + clear ip pim interfaces Reset PIM interfaces + clear ip pim oil Rescan PIM OIL (output interface list) + debug igmp IGMP protocol activity + debug mroute PIM interaction with kernel MFC cache + debug pim PIM protocol activity + debug pim zebra ZEBRA protocol activity + debug ssmpingd ssmpingd activity + show debugging State of each debugging option + test igmp receive report Test reception of IGMPv3 report + test pim receive assert Test reception of PIM assert + test pim receive dump Test reception of PIM packet dump + test pim receive hello Test reception of PIM hello + test pim receive join Test reception of PIM join + test pim receive prune Test reception of PIM prune + test pim receive upcall Test reception of kernel upcall + +statistics commands: + pimd: + show memory pim PIM memory statistics + +-x- diff --git a/pimd/COPYING b/pimd/COPYING new file mode 100644 index 00000000..3912109b --- /dev/null +++ b/pimd/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/pimd/DEBUG b/pimd/DEBUG new file mode 100644 index 00000000..72fb8264 --- /dev/null +++ b/pimd/DEBUG @@ -0,0 +1,86 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +DEBUG HINTS + + - Check the source is issuing multicast packets with TTL high enough + to reach the recipients. + + - Check the multicast packets are not being dropped due to + fragmentation problems. + + - Three easy options to test IGMPv3 joins from the receiver host: + + 1) Configure pimd on the receiver host with "ip igmp join": + + interface eth0 + ip pim ssm + ip igmp join 239.1.1.1 1.1.1.1 + + 2) Use test_igmpv3_join command-line utility (provided with qpimd): + + test_igmpv3_join eth0 239.1.1.1 1.1.1.1 + + 3) User the Stig Venaas' ssmping utility: + + ssmping -I eth0 1.1.1.1 + + To see multicast responses with ssmping, you will need run on + the host 1.1.1.1 either: + a) Stig Venaas' ssmpingd command-line daemon + OR + b) qpimd built-in ssmpingd service: + conf t + ip ssmpingd 1.1.1.1 + + - Using nepim to generate multicast stream from 1.1.1.1 to 239.1.1.1: + + Notices: + + a) The host unicast address 1.1.1.1 must be reachable from the + receiver. + + b) nepim tool requires the receiver must be started *before* the + sender. + + First: Start a receiver for that stream by running: + + nepim -q -6 -j 1.1.1.1+239.1.1.1@eth0 + (Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.) + + Second: Start the sender at host 1.1.1.1. + + The following command generates a 100-kbps multicast stream for + channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP + packet (to avoid fragmentation): + + nepim -6 -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d + + + +SAMPLE DEBUG COMMANDS + + conf t + int eth0 + ip pim ssm + + test pim receive hello eth0 192.168.0.2 600 10 111 1000 3000 0 + test pim receive join eth0 600 192.168.0.1 192.168.0.2 239.1.1.1 1.1.1.1 + + show ip pim join + + +INTEROPERABILITY WITH CISCO + + ! Cisco IP Multicast command reference: + ! ftp://ftpeng.cisco.com/ipmulticast/Multicast-Commands + ! + ip pim ssm default ! enable SSM mode for groups 232.0.0.0/8 + ip multicast-routing + ip pim state-refresh disable + no ip pim dm-fallback + ! + interface FastEthernet0 + ip pim sparse-mode + ip igmp version 3 + +-x- diff --git a/pimd/LINUX_KERNEL_MROUTE_MFC b/pimd/LINUX_KERNEL_MROUTE_MFC new file mode 100644 index 00000000..e87e567f --- /dev/null +++ b/pimd/LINUX_KERNEL_MROUTE_MFC @@ -0,0 +1,26 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +# +# The Linux Kernel MFC (Multicast Forwarding Cache) +# + +# Check Linux kernel multicast interfaces: +cat /proc/net/dev_mcast + +# Check that interface eth0 is forwarding multicast: +cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding + +# Check Linux kernel multicast VIFs: +cat /proc/net/ip_mr_vif +Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote + +# Check Linux kernel MFC: +# Oifs format = vifi:TTL +cat /proc/net/ip_mr_cache +Group Origin Iif Pkts Bytes Wrong Oifs + +# iproute2 can display the MFC: +ip mroute show +(2.2.2.2, 239.2.2.2) Iif: eth1 Oifs: eth0 + +# -- end-of-file -- diff --git a/pimd/Makefile.am b/pimd/Makefile.am new file mode 100644 index 00000000..f13debd9 --- /dev/null +++ b/pimd/Makefile.am @@ -0,0 +1,76 @@ +## Process this file with automake to produce Makefile.in. +## $QuaggaId: $Format:%an, %ai, %h$ $ + +# qpimd - pimd for quagga +# Copyright (C) 2008 Everton da Silva Marques +# +# qpimd 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. +# +# qpimd 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 General Public +# License along with qpimd; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 59 Temple Place - Suite +# 330, Boston, MA 02111-1307, USA. + +# PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands +# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging +# PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex +# PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch +# PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries +# PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall + +PIM_DEFS = +#PIM_DEFS += -DPIM_DEBUG_BYDEFAULT +PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY +#PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH +PIM_DEFS += -DPIM_ZCLIENT_DEBUG +PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC +#PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) +INSTALL_SDATA=@INSTALL@ -m 600 +LIBS = @LIBS@ + +AM_CFLAGS = $(PICFLAGS) +AM_LDFLAGS = $(PILDFLAGS) + +noinst_LIBRARIES = libpim.a +sbin_PROGRAMS = pimd +bin_PROGRAMS = test_igmpv3_join + +libpim_a_SOURCES = \ + pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \ + pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \ + pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \ + pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ + pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ + pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \ + pim_igmp_join.c pim_ssmpingd.c pim_int.c + +noinst_HEADERS = \ + pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ + pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \ + pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \ + pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ + pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ + pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \ + pim_igmp_join.h pim_ssmpingd.h pim_int.h + +pimd_SOURCES = \ + pim_main.c $(libpim_a_SOURCES) + +test_igmpv3_join_SOURCES = \ + test_igmpv3_join.c pim_igmp_join.c + +pimd_LDADD = ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = pimd.conf.sample diff --git a/pimd/README b/pimd/README new file mode 100644 index 00000000..1e3f72c8 --- /dev/null +++ b/pimd/README @@ -0,0 +1,164 @@ +# +# $QuaggaId: $Format:%an, %ai, %h$ $ +# + +INTRODUCTION + + qpimd aims to implement a PIM (Protocol Independent Multicast) + daemon for the Quagga Routing Suite. + + Initially qpimd targets only PIM SSM (Source-Specific + Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only + Routers) of RFC 4601. + + In order to deliver end-to-end multicast routing control + plane, qpimd includes the router-side of IGMPv3 (RFC 3376). + +LICENSE + + qpimd - pimd for quagga + Copyright (C) 2008 Everton da Silva Marques + + qpimd 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. + + qpimd 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 General Public + License along with qpimd; see the file COPYING. If not, write + to the Free Software Foundation, Inc., 59 Temple Place - Suite + 330, Boston, MA 02111-1307, USA. + +HOME SITE + + qpimd lives at: + + https://github.com/udhos/qpimd + +PLATFORMS + + qpimd has been tested with Debian Lenny under Linux 2.6. + +REQUIREMENTS + + qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net) + + The GNU Build System (Autotools) is required to build from + source code repository. + + gawk is also needed to build with Autotools. Any other awk + usually won't work. + +BUILDING FROM QUAGGA GIT REPOSITORY + + 1) Get the latest quagga source tree + + # git clone git://code.quagga.net/quagga.git quagga + + 2) Apply qpimd patch into quagga source tree + + # patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch + + 3) Compile and install quagga + + # cd quagga + # ./bootstrap.sh + # ./configure --prefix=/usr/local/quagga --enable-pimd + # make + # make install + +BUILDING FROM QUAGGA TARBALL + + 1) Get the latest quagga tarball + + # wget http://www.quagga.net/download/quagga-0.99.13.tar.gz + + 2) Unpack the quagga tarball + + # tar xzf quagga-0.99.13.tar.gz + + 3) Apply qpimd patch into quagga source tree + + # patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch + + 4) Compile and install quagga + + # cd quagga-0.99.13 + # ./configure --prefix=/usr/local/quagga --enable-pimd + # make + # make install + +USAGE + + 1) Configure and start the zebra daemon + + # cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf + # vi /usr/local/quagga/etc/zebra.conf + # /usr/local/quagga/sbin/zebra + + 2) Configure and start the pimd daemon + + # cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf + # vi /usr/local/quagga/etc/pimd.conf + # /usr/local/quagga/sbin/pimd + + 3) Access pimd vty interface at port TCP 2611 + + # telnet localhost 2611 + +CONFIGURATION COMMANDS + + See available commands in the file pimd/COMMANDS. + +KNOWN CAVEATS + + See list of known caveats in the file pimd/CAVEATS. + +SUPPORT + + Please post comments, questions, patches, bug reports at the + support site: + + https://github.com/udhos/qpimd + +RELATED WORK + + igmprt: An IGMPv3-router implementation + - http://www.loria.fr/~lahmadi/igmpv3-router.html + + USC pimd: PIMv2-SM daemon + - http://netweb.usc.edu/pim/pimd (URL broken in 2008-12-23) + - http://packages.debian.org/source/sid/pimd (from Debian) + + troglobit pimd: This is the original USC pimd from + http://netweb.usc.edu/pim/. In January 16, 2010 it was revived + with the intention to collect patches floating around in + Debian, Gentoo, Lintrack and other distribution repositories + and to provide a central point of collaboration. + - http://github.com/troglobit/pimd + + zpimd: zpimd is not dependent of zebra or any other routing daemon + - ftp://robur.slu.se/pub/Routing/Zebra + - http://sunsite2.icm.edu.pl/pub/unix/routing/zpimd + + mrd6: an IPv6 Multicast Router for Linux systems + - http://fivebits.net/proj/mrd6/ + + MBGP: Implementation of RFC 2858 for Quagga + - git://git.coplanar.net/~balajig/quagga + - http://www.gossamer-threads.com/lists/quagga/dev/18000 + +REFERENCES + + IANA Protocol Independent Multicast (PIM) Parameters + http://www.iana.org/assignments/pim-parameters/pim-parameters.txt + + Address Family Numbers + http://www.iana.org/assignments/address-family-numbers + + -- END -- diff --git a/pimd/TODO b/pimd/TODO new file mode 100644 index 00000000..2308573b --- /dev/null +++ b/pimd/TODO @@ -0,0 +1,426 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +T1 DONE Implement debug command + test pim receive join + +T2 DONE Implement debug command + test pim receive prune + +T3 DONE Per-interface Downstream (S,G) state machine + (RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages) + +T4 DONE Upstream (S,G) state machine + (RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages) + +T5 DONE Verify Data Packet Forwarding Rules + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Additionally, the Packet forwarding rules of Section 4.2 can be + simplified in a PIM-SSM-only router: + + iif is the incoming interface of the packet. + oiflist = NULL + if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) { + oiflist = inherited_olist(S,G) + } else if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + oiflist = oiflist (-) iif + forward packet on all interfaces in oiflist + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + +T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1). + Changes in pim_ifchannel.ifassert_winner should trigger + pim_upstream_update_join_desired(). + Depends on TODO T27. + Depends on TODO T33. + See also CAVEAT C7. + See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages + Transitions from Joined State + RPF'(S,G) changes due to an Assert + + http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html: + + The PIM Assert mechanism is used to shutoff duplicate flows onto + the same multiaccess network. Routers detect this condiction when + they receive an (S,G) packet via a multi-access interface that is + in the (S,G) OIL. This causes the routers to send Assert + Messages. + + Note that neighbors will not accept Join/Prune or Assert messages + from a router unless they have first heard a Hello message from that + router. Thus, if a router needs to send a Join/Prune or Assert + message on an interface on which it has not yet sent a Hello message + with the currently configured IP address, then it MUST immediately + send the relevant Hello message without waiting for the Hello Timer + to expire, followed by the Join/Prune or Assert message. + +T7 DONE Implement hello option: LAN Prune Delay + +T8 DONE Implement J/P_Override_Interval(I) + Depends on TODO T7. + See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval. + +T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update. + channel_oil vif index accordingly ? + Beware accidentaly adding looped MFC entries (IIF=OIF). + +T10 DONE React to (S,G) join directed to another upstream address. See + also: + + RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages + + If a router wishes to propagate a Join(S,G) upstream, it must also + watch for messages on its upstream interface from other routers on + that subnet, and these may modify its behavior. If it sees a + Join(S,G) to the correct upstream neighbor, it should suppress its + own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or + Prune(*,G) to the correct upstream neighbor towards S, it should + be prepared to override that prune by scheduling a Join(S,G) to be + sent almost immediately. + +T11 DONE Review protocol modifications for SSM + (RFC 4601 4.8.1. Protocol Modifications for SSM Destination + Addresses) + +T12 DONE Review updates of RPF entries. + FIXME pim_upstream.c send_join(): + Currently only one upstream state is affected by detection of RPF change. + RPF change should affect all upstream states sharing the RPF cache. + +T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually + implemented with this strategy: + rpf_ifch=find_ifch(up->rpf->interface). + See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example. + + $ grep -i macro pimd/*.c + pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros + pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros + pimd/pim_ifchannel.c: Macro: + pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros + +T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. + See pim_mroute.c mroute_msg(). + +T15 DONE Interface command to statically join (S,G). + interface eth0 + ip igmp join-group 239.1.1.1 source 1.1.1.1 + +T16 DONE RPF'(S,G) lookup is not working for S reachable with default route. + See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c. + Zebra daemon RIB is not reflecting changes in kernel routes + accurately? + +T17 DONE Prevent CLI from creating bogus interfaces. + Example: + conf t + interface xxx + +T18 Consider reliable pim solution (refresh reduction) + A Reliable Transport Mechanism for PIM + http://tools.ietf.org/wg/pim/draft-ietf-pim-port/ + PORT=PIM-Over-Reliable-Transport + +T19 DONE Fix self as neighbor + See mailing list post: + http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html + +T20 DONE Fix debug message: "pim_neighbor_update: internal error: + trying to replace same prefix list" + See mailing list post: + http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html + +T21 DONE Clean-up PIM/IGMP interface mismatch debugging + See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am + See mailing list post: + http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html + +T22 DONE IGMP must be protected against adding looped MFC entries + created by both source and receiver attached to the same + interface. + +T23 DONE libzebra crash after zclient_lookup_nexthop. + See mailing list post: + http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html + +T24 DONE zserv may return recursive routes: + - nexthop type is set to ZEBRA_NEXTHOP_IPV4 + - ifindex is not reported + - calls expecting ifindex (fib_lookup_if_vif_index) are disrupted + See also this mailing list post: + [PATCH 21/21] Link detect and recursive routes + http://www.gossamer-threads.com/lists/quagga/dev/17564 + +T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32 + See also: + pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32 + zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32 + +T26 DONE Zebra daemon is marking recursive static route as inactive. + + FIXED: zebra daemon was incorrectly marking recursive routes + pointing to kernel routes as inactive: + zebra/zebra_rib.c nexthop_active_ipv4: + -- Original: + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) + -- Fixed: + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || + match->type == ZEBRA_ROUTE_KERNEL) + + Old problem description: + + This prevents rib_match_ipv4 from returning its nexthop: + client: pim_zlookup.c zclient_read_nexthop + server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4 + + Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4 + Examples: + rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0); + rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0); + + This patch didn't fix the issue: + [PATCH 21/21] Link detect and recursive routes + http://www.gossamer-threads.com/lists/quagga/dev/17564 + + See the example below for the route 2.2.2.2. + +bash# route add -host 1.1.1.1 gw 127.0.0.1 +bash# route add -host 2.2.2.2 gw 1.1.1.1 +bash# netstat -nvr +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo +1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo +192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0 +bash# + +zebra# sh ip route +Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, + I - ISIS, B - BGP, > - selected route, * - FIB route + +K>* 0.0.0.0/0 via 192.168.0.2, eth0 +K>* 1.1.1.1/32 via 127.0.0.1, lo +K * 2.2.2.2/32 via 1.1.1.1, lo inactive +C>* 127.0.0.0/8 is directly connected, lo +C>* 192.168.0.0/24 is directly connected, eth0 + +quagga-pimd-router# sh ip route 1.1.1.1 +Address NextHop Interface Metric Preference +1.1.1.1 127.0.0.1 lo 0 0 +quagga-pimd-router# +quagga-pimd-router# sh ip route 2.2.2.2 +Address NextHop Interface Metric Preference +2.2.2.2 192.168.0.2 eth0 0 0 +quagga-pimd-router# + +T27 DONE Implement debug command + test pim receive assert + See also TODO T6: (S,G) Assert state machine. + +T28 DONE Bad IPv4 address family=02 in Join/Prune dump + Reported by Andrew Lunn + + # 58-byte pim v2 Join/Prune dump + # ------------------------------ + # IPv4 address family=02 is wrong, correct IPv4 address family is 01 + # See http://www.iana.org/assignments/address-family-numbers + # + c8XX YY03 : ip src 200.xx.yy.3 + e000 000d : ip dst 224.0.0.13 + 9404 0000 : ip router alert option 148.4.0.0 + 2300 ab13 : pimv2,type=3 res=00 checksum=ab13 + 0200 : upstream family=02, encoding=00 + c8XX YY08 : upstream 200.xx.yy.8 + 0001 00d2 : res=00 groups=01 holdtime=00d2 + 0200 0020 : group family=02, encoding=00, res=00, mask_len=20 + ef01 0101 : group address 239.1.1.1 + 0001 0000 : joined=0001 pruned=0000 + 0200 0020 : source family=02, encoding=00, res=00, mask_len=20 + 0101 0101 : source address 1.1.1.1 + +T29 DONE Reset interface PIM-hello-sent counter when primary address changes + See pim_ifp->pim_ifstat_hello_sent + + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + +T30 DONE Run interface DR election when primary address changes + Reported by Andrew Lunn + See pim_if_dr_election(). + +T31 If an interface changes one of its secondary IP addresses, a Hello + message with an updated Address_List option and a non-zero + HoldTime should be sent immediately. + See also detect_secondary_address_change + See also CAVEAT C15. + See also RFC 4601: 4.3.1. Sending Hello Messages + +T32 FIXED Detection of interface primary address changes may fail when + there are multiple addresses. + See also CAVEAT C14. + + pim_find_primary_addr() should return interface primary address + from connected list. Currently it returns the first address. + + Zebra daemon "show int" is able to keep the primary address as + first address. + +T33 DONE Implement debug command: test pim receive upcall + See also TODO T6: (S,G) Assert state machine. + +T34 DONE assert_action_a1 + +T35 DONE Review macros depending on interface I. + + See also: grep ,I\) pimd/*.c + + For the case (S,G,I) check if I is either + 1) interface attached to this per-interface S,G state (don't think so) + or + 2) an arbitrary interface (most probably) + + For the arbitrary interface case (2), consider representing + interface ifp as its primary address (struct in_addr ifaddr). The + benefit is in_addr does not need to be dereferenced, so it does + not demand protection against crashes. + +T36 DONE React to zebra daemon link-detect up/down notification. + pim_ifp->primary_address is managed by detect_primary_address_change() + depending on to ifp->connected (managed by zebra_interface_address_read()). + +T37 DONE Review list of variables which may affect pim_upstream.c + pim_upstream_evaluate_join_desired(). + Call pim_upstream_update_join_desired() accordingly. + + See the order of invokation: + pim_if_dr_election(ifp); + pim_if_update_join_desired(pim_ifp); /* depends on DR */ + pim_if_update_could_assert(ifp); /* depends on DR */ + pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ + + join_desired depends on: + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface + +T38 DONE Detect change in AssertTrackingDesired(S,G,I) + + See the order of invokation: + dr_election: none + update_join_desired: depends on DR + update_tracking_desired: depends on DR, join_desired + + AssertTrackingDesired(S,G,I) depends on: + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->local_ifmembership + ch->ifassert_winner + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.interface + PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags) + +T39 DONE AssertTrackingDesired: flags is not matching evaluation + + # show ip pim assert-internal + CA: CouldAssert + ECA: Evaluate CouldAssert + ATD: AssertTrackingDesired + eATD: Evaluate AssertTrackingDesired + + Interface Address Source Group CA eCA ATD eATD + eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes + # + +T40 Lightweight MLDv2 + http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05 + http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt + http://www.ietf.org/html.charters/mboned-charter.html + +T41 DONE ssmping support + + See also: + http://www.venaas.no/multicast/ssmping/ + draft-ietf-mboned-ssmping-07 + http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07 + + Example: + + debug ssmpingd + + conf t + ip ssmpingd 1.1.1.1 + + show ip ssmpingd + +T42 Static igmp join fails when loading config at boot time + + ! Wrong behavior seen at boot time: + ! + 2010/02/22 08:59:00 PIM: igmp_source_forward_start: ignoring request for + looped MFC entry (S,G)=(3.3.3.3,239.3.3.3): igmp_sock=12 oif=eth0 vif_index=2 + + ! Correct behavior seen later: + ! + 2010/02/22 09:03:16 PIM: igmp_source_forward_start: ignoring request for + looped MFC entry (S,G)=(2.2.2.2,239.2.2.2): igmp_sock=17 oif=lo vif_index=1 + + ! To see the wrong message at boot: + ! + debug igmp trace + ! + interface lo + ip igmp + ip igmp join 239.2.2.2 2.2.2.2 + ip igmp join 239.3.3.3 3.3.3.3 + ! + + ! Interfaces indexes: + Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut + eth0 200.202.112.3 2 2 0 0 0 0 + lo 127.0.0.1 1 1 0 0 0 0 + +T43 PIM Neighbor Reduction + https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/ + + "In a transit LAN (no directly connected source or receiver), many + of the PIM procedures don't apply. (...) This proposal describes + a procedure to reduce the amount of neighbors established over a + transit LAN." + +T44 Single Stream Multicast Fast Reroute (SMFR) Method + https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/ + + "This document proposes an IP multicast fast convergence method + based on differentiating primary and backup PIM join." + +T45 RFC5384 - The Join Attribute Format + "This document describes a modification of the Join message that + allows a node to associate attributes with a particular tree." + +T46 PIM Multi-Topology ID (MT-ID) Join-Attribute + http://tools.ietf.org/html/draft-cai-pim-mtid-00 + Depends on T45. + + "This draft introduces a new type of PIM Join Attribute used to + encode the identity of the topology PIM uses for RPF." + +-x- diff --git a/pimd/TROUBLESHOOTING b/pimd/TROUBLESHOOTING new file mode 100644 index 00000000..7d1f52de --- /dev/null +++ b/pimd/TROUBLESHOOTING @@ -0,0 +1,33 @@ +TROUBLESHOOTING + +# Check kernel mcast cache +# On Linux: +ip mroute show + +! qpimd on last-hop router +! . attached to mcast receiver +! . runnning both PIM-SSM and IGMPv3 +! +show ip mroute (kernel mcast programming is correct?) +show ip pim upstream (we joined our upstream?) +show ip pim neighbor (upstream is neighbor?) +show ip pim interface (pim enabled on interfaces?) +show ip multicast (multicast enabled at all?) +show ip rib SRC (unicast route towards source?) + +show ip igmp sources (receiver joined on interface?) +show ip igmp interface (igmp enabled on receiver interface?) + +! qpimd on intermmediate routers +! . may be attached to mcast source +! . runnning only PIM-SSM, not IGMPv3 +! +show ip mroute (kernel mcast programming is correct?) +show ip pim upstream (we joined our upstream?) +show ip pim join (downstream joined us?) +show ip pim neighbor (downstream is neighbor?) +show ip pim interface (pim enabled on interfaces?) +show ip multicast (multicast enabled at all?) +show ip rib SRC (unicast route towards source?) + +--EOF-- diff --git a/pimd/WHY_SSM b/pimd/WHY_SSM new file mode 100644 index 00000000..2e8c966f --- /dev/null +++ b/pimd/WHY_SSM @@ -0,0 +1,32 @@ +WHY SSM + +Benefis of PIM SSM over PIM SM +------------------------------ + +- SSM consumes minimum link bandwidth +- SSM simplifies multicast address management (specially important for + inter-domain multicast) + - SSM (S,G) channels easily provide unique per-application addressing + - SSM does not require MSDP between PIM domains +- SSM does not suffer instabilities from traffic-driven SPT switchover +- SSM is not suscetible to DoS attack from unwanted sources +- SSM does not use RP. Some RP issues: + - RP is possible point of failure + - RP demands redundancy management + - RP may require PIM dense mode support for RP election + - RP is possible performance bottleneck + - RP may demand lots of extra management +- SSM can be deployed in an existing PIM SM network (only the last hop + routers need to support IGMPv3) +- SSM is easier to deploy and maintain + +PIM-SSM drawbacks +----------------- + +- SSM requires IGMPv3 support on both receivers and last-hop routers +- SSM may be memory intensive when managing (S,G) states for + many-to-many multicast distribution +- SSM will keep (S,G) state as long as there are subscriptions from + receivers, even if the source is not actually sending traffic + +--EOF-- diff --git a/pimd/git-clone-github.sh b/pimd/git-clone-github.sh new file mode 100755 index 00000000..ae2362a7 --- /dev/null +++ b/pimd/git-clone-github.sh @@ -0,0 +1,27 @@ +#! /bin/bash +# +# Github Developer Git Checkout +# +# Delete remote branch qpimd: git push origin :qpimd +# (git push origin :refs/heads/branch_to_delete) +# Delete remote tag v0.139: git push origin :v0.139 +# (git push origin :refs/tags/tag_to_delete) +# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142 +# Rename branch qpimd to pim: git branch -m qpimd pim +# Commit changes: git commit -a +# Send changes: git push --all +# +# Recipe to re-sync with Quagga repository: +# git clone https://github.com/udhos/qpimd quagga +# cd quagga +# git checkout master +# git pull http://git.sv.gnu.org/r/quagga.git master +# git checkout -b pim origin/pim +# git rebase master pim +# # Test, then push back into Github repository: +# git push origin :pim ;# delete remote branch pim +# git push --all +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +git clone https://github.com/udhos/qpimd diff --git a/pimd/git-clone-savannah.sh b/pimd/git-clone-savannah.sh new file mode 100755 index 00000000..1aad51bb --- /dev/null +++ b/pimd/git-clone-savannah.sh @@ -0,0 +1,27 @@ +#! /bin/bash +# +# Savannah Developer Git Checkout +# +# Delete remote branch qpimd: git push origin :qpimd +# (git push origin :refs/heads/branch_to_delete) +# Delete remote tag v0.139: git push origin :v0.139 +# (git push origin :refs/tags/tag_to_delete) +# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142 +# Rename branch qpimd to pim: git branch -m qpimd pim +# Commit changes: git commit -a +# Send changes: git push --all +# +# Recipe to re-sync with Quagga repository: +# git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga +# cd quagga +# git checkout master +# git pull git://code.quagga.net/quagga.git master +# git checkout -b pim origin/pim +# git rebase master pim +# # Test, then push back into Savannah repository: +# git push origin :pim ;# delete remote branch pim +# git push --all +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c new file mode 100644 index 00000000..ad21e08e --- /dev/null +++ b/pimd/pim_assert.c @@ -0,0 +1,808 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_int.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_hello.h" +#include "pim_macro.h" +#include "pim_assert.h" +#include "pim_ifchannel.h" + +static int assert_action_a3(struct pim_ifchannel *ch); +static void assert_action_a2(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric); +static void assert_action_a6(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric); + +void pim_ifassert_winner_set(struct pim_ifchannel *ch, + enum pim_ifassert_state new_state, + struct in_addr winner, + struct pim_assert_metric winner_metric) +{ + int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr); + int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric, + &winner_metric); + + if (PIM_DEBUG_PIM_EVENTS) { + if (ch->ifassert_state != new_state) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + pim_ifchannel_ifassert_name(new_state), + ch->interface->name); + } + + if (winner_changed) { + char src_str[100]; + char grp_str[100]; + char was_str[100]; + char winner_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); + pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); + zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + was_str, winner_str, ch->interface->name); + } + } /* PIM_DEBUG_PIM_EVENTS */ + + ch->ifassert_state = new_state; + ch->ifassert_winner = winner; + ch->ifassert_winner_metric = winner_metric; + ch->ifassert_creation = pim_time_monotonic_sec(); + + if (winner_changed || metric_changed) { + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); + } +} + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static int preferred_assert(const struct pim_ifchannel *ch, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(recv_metric, + &ch->ifassert_winner_metric); +} + +static int acceptable_assert(const struct pim_assert_metric *my_metric, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(recv_metric, + my_metric); +} + +static int inferior_assert(const struct pim_assert_metric *my_metric, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(my_metric, + recv_metric); +} + +static int cancel_assert(const struct pim_assert_metric *recv_metric) +{ + return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) + && + (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX); +} + +static void if_could_assert_do_a1(const char *caller, + struct pim_ifchannel *ch) +{ + if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + if (assert_action_a1(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + __PRETTY_FUNCTION__, caller, + src_str, grp_str, ch->interface->name); + /* log warning only */ + } + } +} + +static int dispatch_assert(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr, + struct pim_assert_metric recv_metric) +{ + struct pim_ifchannel *ch; + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ifp->name); + return -1; + } + + switch (ch->ifassert_state) { + case PIM_IFASSERT_NOINFO: + if (recv_metric.rpt_bit_flag) { + /* RPT bit set */ + if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + } + else { + /* RPT bit clear */ + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + } + else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) { + if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) { + assert_action_a6(ch, recv_metric); + } + } + } + break; + case PIM_IFASSERT_I_AM_WINNER: + if (preferred_assert(ch, &recv_metric)) { + assert_action_a2(ch, recv_metric); + } + else { + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + assert_action_a3(ch); + } + } + break; + case PIM_IFASSERT_I_AM_LOSER: + if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) { + /* Assert from current winner */ + + if (cancel_assert(&recv_metric)) { + assert_action_a5(ch); + } + else { + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + assert_action_a5(ch); + } + else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) { + if (!recv_metric.rpt_bit_flag) { + assert_action_a2(ch, recv_metric); + } + } + } + } + else if (preferred_assert(ch, &recv_metric)) { + assert_action_a2(ch, recv_metric); + } + break; + default: + { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->ifassert_state, ifp->name); + } + return -2; + } + + return 0; +} + +int pim_assert_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + uint8_t *buf, int buf_size) +{ + struct prefix msg_group_addr; + struct prefix msg_source_addr; + struct pim_assert_metric msg_metric; + int offset; + uint8_t *curr; + int curr_size; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + curr = buf; + curr_size = buf_size; + + /* + Parse assert group addr + */ + offset = pim_parse_addr_group(ifp->name, src_addr, + &msg_group_addr, + curr, curr_size); + if (offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -1; + } + curr += offset; + curr_size -= offset; + + /* + Parse assert source addr + */ + offset = pim_parse_addr_ucast(ifp->name, src_addr, + &msg_source_addr, + curr, curr_size); + if (offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -2; + } + curr += offset; + curr_size -= offset; + + if (curr_size != 8) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s", + __PRETTY_FUNCTION__, + curr_size, + src_str, ifp->name); + return -3; + } + + /* + Parse assert metric preference + */ + + msg_metric.metric_preference = pim_read_uint32_host(curr); + + msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */ + msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */ + + curr += 4; + + /* + Parse assert route metric + */ + + msg_metric.route_metric = pim_read_uint32_host(curr); + + if (PIM_DEBUG_PIM_TRACE) { + char neigh_str[100]; + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", src_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", msg_source_addr.u.prefix4, source_str, sizeof(source_str)); + pim_inet4_dump("", msg_group_addr.u.prefix4, group_str, sizeof(group_str)); + zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", + __PRETTY_FUNCTION__, neigh_str, ifp->name, + source_str, group_str, + msg_metric.metric_preference, + msg_metric.route_metric, + PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag)); + } + + msg_metric.ip_address = src_addr; + + return dispatch_assert(ifp, + msg_source_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_metric); +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + Assert metrics are defined as: + + When comparing assert_metrics, the rpt_bit_flag, metric_preference, + and route_metric field are compared in order, where the first lower + value wins. If all fields are equal, the primary IP address of the + router that sourced the Assert message is used as a tie-breaker, + with the highest IP address winning. +*/ +int pim_assert_metric_better(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2) +{ + if (m1->rpt_bit_flag < m2->rpt_bit_flag) + return 1; + if (m1->rpt_bit_flag > m2->rpt_bit_flag) + return 0; + + if (m1->metric_preference < m2->metric_preference) + return 1; + if (m1->metric_preference > m2->metric_preference) + return 0; + + if (m1->route_metric < m2->route_metric) + return 1; + if (m1->route_metric > m2->route_metric) + return 0; + + return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr); +} + +int pim_assert_metric_match(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2) +{ + if (m1->rpt_bit_flag != m2->rpt_bit_flag) + return 0; + if (m1->metric_preference != m2->metric_preference) + return 0; + if (m1->route_metric != m2->route_metric) + return 0; + + return m1->ip_address.s_addr == m2->ip_address.s_addr; +} + +int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, + struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr, + uint32_t metric_preference, + uint32_t route_metric, + uint32_t rpt_bit_flag) +{ + uint8_t *buf_pastend = pim_msg + buf_size; + uint8_t *pim_msg_curr; + int pim_msg_size; + int remain; + + pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */ + + /* Encode group */ + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: failure encoding group address %s: space left=%d", + __PRETTY_FUNCTION__, group_str, remain); + return -1; + } + + /* Encode source */ + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure encoding source address %s: space left=%d", + __PRETTY_FUNCTION__, source_str, remain); + return -2; + } + + /* Metric preference */ + pim_write_uint32(pim_msg_curr, rpt_bit_flag ? + metric_preference | 0x80000000 : + metric_preference); + pim_msg_curr += 4; + + /* Route metric */ + pim_write_uint32(pim_msg_curr, route_metric); + pim_msg_curr += 4; + + /* + Add PIM header + */ + pim_msg_size = pim_msg_curr - pim_msg; + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_ASSERT); + + return pim_msg_size; +} + +static int pim_assert_do(struct pim_ifchannel *ch, + struct pim_assert_metric metric) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + uint8_t pim_msg[1000]; + int pim_msg_size; + + ifp = ch->interface; + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: pim not enabled on interface: %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp, + ch->group_addr, ch->source_addr, + metric.metric_preference, + metric.route_metric, + metric.rpt_bit_flag); + if (pim_msg_size < 1) { + zlog_warn("%s: failure building PIM assert message: msg_size=%d", + __PRETTY_FUNCTION__, pim_msg_size); + return -2; + } + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ + pim_hello_require(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", + __PRETTY_FUNCTION__, + ifp->name, source_str, group_str, + metric.metric_preference, + metric.route_metric, + PIM_FORCE_BOOLEAN(metric.rpt_bit_flag)); + } + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -3; + } + + return 0; +} + +int pim_assert_send(struct pim_ifchannel *ch) +{ + return pim_assert_do(ch, ch->ifassert_my_metric); +} + +/* + RFC 4601: 4.6.4. AssertCancel Messages + + An AssertCancel(S,G) is an infinite metric assert with the RPT bit + set that names S as the source. + */ +static int pim_assert_cancel(struct pim_ifchannel *ch) +{ + struct pim_assert_metric metric; + + metric.rpt_bit_flag = 0; + metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX; + metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX; + metric.ip_address = ch->source_addr; + + return pim_assert_do(ch, metric); +} + +static int on_assert_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + struct interface *ifp; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ifp = ch->interface; + zassert(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + } + + ch->t_ifassert_timer = 0; + + switch (ch->ifassert_state) { + case PIM_IFASSERT_I_AM_WINNER: + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + assert_action_a3(ch); + break; + case PIM_IFASSERT_I_AM_LOSER: + assert_action_a5(ch); + break; + default: + { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->ifassert_state, ifp->name); + } + } + + return 0; +} + +static void assert_timer_off(struct pim_ifchannel *ch) +{ + struct interface *ifp; + + zassert(ch); + ifp = ch->interface; + zassert(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + if (ch->t_ifassert_timer) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + } + } + THREAD_OFF(ch->t_ifassert_timer); + zassert(!ch->t_ifassert_timer); +} + +static void pim_assert_timer_set(struct pim_ifchannel *ch, + int interval) +{ + struct interface *ifp; + + zassert(ch); + ifp = ch->interface; + zassert(ifp); + + assert_timer_off(ch); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, interval, ifp->name); + } + + THREAD_TIMER_ON(master, ch->t_ifassert_timer, + on_assert_timer, + ch, interval); +} + +static void pim_assert_timer_reset(struct pim_ifchannel *ch) +{ + pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A1: Send Assert(S,G). + Set Assert Timer to (Assert_Time - Assert_Override_Interval). + Store self as AssertWinner(S,G,I). + Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I). +*/ +int assert_action_a1(struct pim_ifchannel *ch) +{ + struct interface *ifp = ch->interface; + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -1; /* must return since pim_ifp is used below */ + } + + /* Switch to I_AM_WINNER before performing action_a3 below */ + pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER, + pim_ifp->primary_address, + pim_macro_spt_assert_metric(&ch->upstream->rpf, + pim_ifp->primary_address)); + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + if (assert_action_a3(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + /* warning only */ + } + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + return 0; +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A2: Store new assert winner as AssertWinner(S,G,I) and assert + winner metric as AssertWinnerMetric(S,G,I). + Set Assert Timer to Assert_Time. +*/ +static void assert_action_a2(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric) +{ + pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER, + winner_metric.ip_address, + winner_metric); + + pim_assert_timer_set(ch, PIM_ASSERT_TIME); + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A3: Send Assert(S,G). + Set Assert Timer to (Assert_Time - Assert_Override_Interval). +*/ +static int assert_action_a3(struct pim_ifchannel *ch) +{ + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + pim_assert_timer_reset(ch); + + if (pim_assert_send(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + + zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return -1; + } + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + return 0; +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A4: Send AssertCancel(S,G). + Delete assert info (AssertWinner(S,G,I) and + AssertWinnerMetric(S,G,I) will then return their default + values). +*/ +void assert_action_a4(struct pim_ifchannel *ch) +{ + if (pim_assert_cancel(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + /* log warning only */ + } + + assert_action_a5(ch); + + zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A5: Delete assert info (AssertWinner(S,G,I) and + AssertWinnerMetric(S,G,I) will then return their default values). +*/ +void assert_action_a5(struct pim_ifchannel *ch) +{ + reset_ifassert_state(ch); + zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A6: Store new assert winner as AssertWinner(S,G,I) and assert + winner metric as AssertWinnerMetric(S,G,I). + Set Assert Timer to Assert_Time. + If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) + set SPTbit(S,G) to TRUE. +*/ +static void assert_action_a6(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric) +{ + assert_action_a2(ch, winner_metric); + + /* + If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set + SPTbit(S,G) to TRUE. + + Notice: For PIM SSM, SPTbit(S,G) is already always true. + */ + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); +} + diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h new file mode 100644 index 00000000..bd3fb3e2 --- /dev/null +++ b/pimd/pim_assert.h @@ -0,0 +1,75 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ASSERT_H +#define PIM_ASSERT_H + +#include + +#include "if.h" + +#include "pim_neighbor.h" +#include "pim_ifchannel.h" + +/* + RFC 4601: 4.11. Timer Values + + Note that for historical reasons, the Assert message lacks a + Holdtime field. Thus, changing the Assert Time from the default + value is not recommended. + */ +#define PIM_ASSERT_OVERRIDE_INTERVAL (3) /* seconds */ +#define PIM_ASSERT_TIME (180) /* seconds */ + +#define PIM_ASSERT_METRIC_PREFERENCE_MAX (0xFFFFFFFF) +#define PIM_ASSERT_ROUTE_METRIC_MAX (0xFFFFFFFF) + +void pim_ifassert_winner_set(struct pim_ifchannel *ch, + enum pim_ifassert_state new_state, + struct in_addr winner, + struct pim_assert_metric winner_metric); + +int pim_assert_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + uint8_t *buf, int buf_size); + +int pim_assert_metric_better(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2); +int pim_assert_metric_match(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2); + +int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, + struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr, + uint32_t metric_preference, + uint32_t route_metric, + uint32_t rpt_bit_flag); + +int pim_assert_send(struct pim_ifchannel *ch); + +int assert_action_a1(struct pim_ifchannel *ch); +void assert_action_a4(struct pim_ifchannel *ch); +void assert_action_a5(struct pim_ifchannel *ch); + +#endif /* PIM_ASSERT_H */ diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c new file mode 100644 index 00000000..6b2ac664 --- /dev/null +++ b/pimd/pim_cmd.c @@ -0,0 +1,4500 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include + +#include "command.h" +#include "if.h" +#include "prefix.h" +#include "zclient.h" + +#include "pimd.h" +#include "pim_cmd.h" +#include "pim_iface.h" +#include "pim_vty.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_igmp.h" +#include "pim_igmpv3.h" +#include "pim_sock.h" +#include "pim_time.h" +#include "pim_util.h" +#include "pim_oil.h" +#include "pim_neighbor.h" +#include "pim_pim.h" +#include "pim_ifchannel.h" +#include "pim_hello.h" +#include "pim_msg.h" +#include "pim_upstream.h" +#include "pim_rpf.h" +#include "pim_macro.h" +#include "pim_ssmpingd.h" +#include "pim_zebra.h" + +static struct cmd_node pim_global_node = { + PIM_NODE, + "", + 1 /* vtysh ? yes */ +}; + +static struct cmd_node interface_node = { + INTERFACE_NODE, + "%s(config-if)# ", + 1 /* vtysh ? yes */ +}; + +static void pim_if_membership_clear(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (PIM_IF_TEST_PIM(pim_ifp->options) && + PIM_IF_TEST_IGMP(pim_ifp->options)) { + return; + } + + pim_ifchannel_membership_clear(ifp); +} + +/* + When PIM is disabled on interface, IGMPv3 local membership + information is not injected into PIM interface state. + + The function pim_if_membership_refresh() fetches all IGMPv3 local + membership information into PIM. It is intented to be called + whenever PIM is enabled on the interface in order to collect missed + local membership information. + */ +static void pim_if_membership_refresh(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + if (!PIM_IF_TEST_IGMP(pim_ifp->options)) + return; + + /* + First clear off membership from all PIM (S,G) entries on the + interface + */ + + pim_ifchannel_membership_clear(ifp); + + /* + Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on + the interface + */ + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + struct listnode *grpnode; + struct igmp_group *grp; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + + if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) { + pim_ifchannel_local_membership_add(ifp, + src->source_addr, + grp->group_addr); + } + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + + /* + Finally delete every PIM (S,G) entry lacking all state info + */ + + pim_ifchannel_delete_on_noinfo(ifp); + +} + +static void pim_show_assert(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group State Winner Uptime Timer%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char winner_str[100]; + char uptime[10]; + char timer[10]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->ifassert_winner, + winner_str, sizeof(winner_str)); + + pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation); + pim_time_timer_to_mmss(timer, sizeof(timer), + ch->t_ifassert_timer); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + winner_str, + uptime, + timer, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_internal(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "CA: CouldAssert%s" + "ECA: Evaluate CouldAssert%s" + "ATD: AssertTrackingDesired%s" + "eATD: Evaluate AssertTrackingDesired%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, + "Interface Address Source Group CA eCA ATD eATD%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no", + pim_macro_ch_could_assert_eval(ch) ? "yes" : "no", + PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no", + pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no", + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_metric(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group RPT Pref Metric Address %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char addr_str[100]; + struct pim_assert_metric am; + + am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am.ip_address, + addr_str, sizeof(addr_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am.rpt_bit_flag ? "yes" : "no", + am.metric_preference, + am.route_metric, + addr_str, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_winner_metric(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group RPT Pref Metric Address %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char addr_str[100]; + struct pim_assert_metric *am; + char pref_str[5]; + char metr_str[7]; + + am = &ch->ifassert_winner_metric; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am->ip_address, + addr_str, sizeof(addr_str)); + + if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) + snprintf(pref_str, sizeof(pref_str), "INFI"); + else + snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference); + + if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX) + snprintf(metr_str, sizeof(metr_str), "INFI"); + else + snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am->rpt_bit_flag ? "yes" : "no", + pref_str, + metr_str, + addr_str, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_membership(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group Membership%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ? + "NOINFO" : "INCLUDE", + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ + +} + +static void igmp_show_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char uptime[10]; + int mloop; + + pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation); + + mloop = pim_socket_mcastloop_get(igmp->fd); + + vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", + ifp->name, + inet_ntoa(igmp->ifaddr), + ifp->ifindex, + igmp->fd, + uptime, + if_is_multicast(ifp) ? "yes" : "no", + if_is_broadcast(ifp) ? "yes" : "no", + (mloop < 0) ? "?" : (mloop ? "yes" : "no"), + (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", + (ifp->flags & IFF_PROMISC) ? "yes" : "no", + PIM_IF_IS_DELETED(ifp) ? "yes" : "no", + VTY_NEWLINE); + } + } +} + +static void igmp_show_interface_join(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group Socket Uptime %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct listnode *join_node; + struct igmp_join *ij; + struct in_addr pri_addr; + char pri_addr_str[100]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (!pim_ifp->igmp_join_list) + continue; + + pri_addr = pim_find_primary_addr(ifp); + pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) { + char group_str[100]; + char source_str[100]; + char uptime[10]; + + pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation); + pim_inet4_dump("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ij->source_addr, source_str, sizeof(source_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %6d %8s%s", + ifp->name, + pri_addr_str, + source_str, + group_str, + ij->sock_fd, + uptime, + VTY_NEWLINE); + } /* for (pim_ifp->igmp_join_list) */ + + } /* for (iflist) */ + +} + +static void show_interface_address(struct vty *vty) +{ + struct listnode *ifpnode; + struct interface *ifp; + + vty_out(vty, + "Interface Primary Secondary %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifpnode, ifp)) { + struct listnode *ifcnode; + struct connected *ifc; + struct in_addr pri_addr; + char pri_addr_str[100]; + + pri_addr = pim_find_primary_addr(ifp); + + pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) { + char sec_addr_str[100]; + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if (p->u.prefix4.s_addr == pri_addr.s_addr) { + sec_addr_str[0] = '\0'; + } + else { + pim_inet4_dump("", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str)); + } + + vty_out(vty, "%-9s %-15s %-15s%s", + ifp->name, + pri_addr_str, + sec_addr_str, + VTY_NEWLINE); + } + } +} + +static void pim_show_dr(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "NonPri: Number of neighbors missing DR Priority hello option%s%s", + VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address DR Uptime Elections Changes NonPri%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char dr_str[100]; + char dr_uptime[10]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), + now, pim_ifp->pim_dr_election_last); + + pim_inet4_dump("", pim_ifp->pim_dr_addr, + dr_str, sizeof(dr_str)); + + vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d%s", + ifp->name, + inet_ntoa(ifaddr), + dr_str, + dr_uptime, + pim_ifp->pim_dr_election_count, + pim_ifp->pim_dr_election_changes, + pim_ifp->pim_dr_num_nondrpri_neighbors, + VTY_NEWLINE); + } +} + +static void pim_show_hello(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char hello_period[10]; + char hello_timer[10]; + char stat_uptime[10]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer); + pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period); + pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start); + + vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s", + ifp->name, + inet_ntoa(ifaddr), + hello_period, + hello_timer, + stat_uptime, + pim_ifp->pim_ifstat_hello_recv, + pim_ifp->pim_ifstat_hello_recvfail, + pim_ifp->pim_ifstat_hello_sent, + pim_ifp->pim_ifstat_hello_sendfail, + VTY_NEWLINE); + } +} + +static void pim_show_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char uptime[10]; + int mloop; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation); + + mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); + + vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", + ifp->name, + inet_ntoa(ifaddr), + ifp->ifindex, + pim_ifp->pim_sock_fd, + uptime, + if_is_multicast(ifp) ? "yes" : "no", + if_is_broadcast(ifp) ? "yes" : "no", + (mloop < 0) ? "?" : (mloop ? "yes" : "no"), + (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", + (ifp->flags & IFF_PROMISC) ? "yes" : "no", + PIM_IF_IS_DELETED(ifp) ? "yes" : "no", + VTY_NEWLINE); + } +} + +static void pim_show_join(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group State Uptime Expire Prune%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char uptime[10]; + char expire[10]; + char prune[10]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + + pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation); + pim_time_timer_to_mmss(expire, sizeof(expire), + ch->t_ifjoin_expiry_timer); + pim_time_timer_to_mmss(prune, sizeof(prune), + ch->t_ifjoin_prune_pending_timer); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + pim_ifchannel_ifjoin_name(ch->ifjoin_state), + uptime, + expire, + prune, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ + +} + +static void pim_show_neighbors(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s" + " T=can_disable_join_suppression%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address Neighbor Uptime Timer Holdt DrPri GenId Recv %s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char uptime[10]; + char holdtime[10]; + char expire[10]; + char neigh_src_str[100]; + char recv[7]; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); + pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime); + pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer); + + recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME) ? 'H' : ' '; + recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? 'L' : ' '; + recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) ? 'P' : ' '; + recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ? 'G' : ' '; + recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST) ? 'A' : ' '; + recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' '; + recv[6] = '\0'; + + vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s", + ifp->name, + inet_ntoa(ifaddr), + neigh_src_str, + uptime, + expire, + holdtime, + neigh->dr_priority, + neigh->generation_id, + recv, + VTY_NEWLINE); + } + + + } +} + +static void pim_show_lan_prune_delay(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, + "PrDly=propagation_delay (msec) OvInt=override_interval (msec)%s" + "HiDly=highest_propagation_delay (msec) HiInt=highest_override_interval (msec)%s" + "NoDly=number_of_non_lan_delay_neighbors%s" + "T=t_bit LPD=lan_prune_delay_hello_option%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T | Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char neigh_src_str[100]; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + + vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s", + ifp->name, + inet_ntoa(ifaddr), + pim_ifp->pim_propagation_delay_msec, + pim_ifp->pim_override_interval_msec, + pim_ifp->pim_number_of_nonlandelay_neighbors, + pim_ifp->pim_neighbors_highest_propagation_delay_msec, + pim_ifp->pim_neighbors_highest_override_interval_msec, + PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)), + neigh_src_str, + PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no", + neigh->propagation_delay_msec, + neigh->override_interval_msec, + PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)), + VTY_NEWLINE); + } + + } +} + +static void pim_show_jp_override_interval(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, + "EffPDelay=effective_propagation_delay (msec)%s" + "EffOvrInt=override_interval (msec)%s" + "JPOvrInt=jp_override_interval (msec)%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s", + ifp->name, + inet_ntoa(ifaddr), + pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled", + pim_if_effective_propagation_delay_msec(ifp), + pim_if_effective_override_interval_msec(ifp), + pim_if_jp_override_interval_msec(ifp), + VTY_NEWLINE); + } +} + +static void pim_show_neighbors_secondary(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, "Interface Address Neighbor Secondary %s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char neigh_src_str[100]; + struct listnode *prefix_node; + struct prefix *p; + + if (!neigh->prefix_list) + continue; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) { + char neigh_sec_str[100]; + + if (p->family != AF_INET) + continue; + + pim_inet4_dump("", p->u.prefix4, + neigh_sec_str, sizeof(neigh_sec_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + neigh_src_str, + neigh_sec_str, + VTY_NEWLINE); + } + } + } +} + +static void pim_show_upstream(struct vty *vty) +{ + struct listnode *upnode; + struct pim_upstream *up; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Source Group State Uptime JoinTimer RefCnt%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { + char src_str[100]; + char grp_str[100]; + char uptime[10]; + char join_timer[10]; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); + pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer); + + vty_out(vty, "%-15s %-15s %-5s %-8s %-9s %6d%s", + src_str, + grp_str, + up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd", + uptime, + join_timer, + up->ref_count, + VTY_NEWLINE); + } +} + +static void pim_show_join_desired(struct vty *vty) +{ + struct listnode *ifnode; + struct listnode *chnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + char src_str[100]; + char grp_str[100]; + + vty_out(vty, + "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s", + VTY_NEWLINE); + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) { + struct pim_upstream *up = ch->upstream; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + + vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s", + ifp->name, + src_str, + grp_str, + pim_macro_ch_lost_assert(ch) ? "yes" : "no", + pim_macro_chisin_joins(ch) ? "yes" : "no", + pim_macro_chisin_pim_include(ch) ? "yes" : "no", + PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags) ? "yes" : "no", + pim_upstream_evaluate_join_desired(up) ? "yes" : "no", + VTY_NEWLINE); + } + } +} + +static void pim_show_upstream_rpf(struct vty *vty) +{ + struct listnode *upnode; + struct pim_upstream *up; + + vty_out(vty, + "Source Group RpfIface RibNextHop RpfAddress %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { + char src_str[100]; + char grp_str[100]; + char rpf_nexthop_str[100]; + char rpf_addr_str[100]; + struct pim_rpf *rpf; + const char *rpf_ifname; + + rpf = &up->rpf; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s", + src_str, + grp_str, + rpf_ifname, + rpf_nexthop_str, + rpf_addr_str, + VTY_NEWLINE); + } +} + +static void show_rpf_refresh_stats(struct vty *vty, time_t now) +{ + char refresh_uptime[10]; + + pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last); + + vty_out(vty, + "RPF Cache Refresh Delay: %ld msecs%s" + "RPF Cache Refresh Timer: %ld msecs%s" + "RPF Cache Refresh Requests: %lld%s" + "RPF Cache Refresh Events: %lld%s" + "RPF Cache Refresh Last: %s%s", + qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, + pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE, + refresh_uptime, VTY_NEWLINE); +} + +static void show_scan_oil_stats(struct vty *vty, time_t now) +{ + char uptime_scan_oil[10]; + char uptime_mroute_add[10]; + char uptime_mroute_del[10]; + + pim_time_uptime_begin(uptime_scan_oil, sizeof(uptime_scan_oil), now, qpim_scan_oil_last); + pim_time_uptime_begin(uptime_mroute_add, sizeof(uptime_mroute_add), now, qpim_mroute_add_last); + pim_time_uptime_begin(uptime_mroute_del, sizeof(uptime_mroute_del), now, qpim_mroute_del_last); + + vty_out(vty, + "Scan OIL - Last: %s Events: %lld%s" + "MFC Add - Last: %s Events: %lld%s" + "MFC Del - Last: %s Events: %lld%s", + uptime_scan_oil, (long long) qpim_scan_oil_events, VTY_NEWLINE, + uptime_mroute_add, (long long) qpim_mroute_add_events, VTY_NEWLINE, + uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE); +} + +static void pim_show_rpf(struct vty *vty) +{ + struct listnode *up_node; + struct pim_upstream *up; + time_t now = pim_time_monotonic_sec(); + + show_rpf_refresh_stats(vty, now); + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, + "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { + char src_str[100]; + char grp_str[100]; + char rpf_addr_str[100]; + char rib_nexthop_str[100]; + const char *rpf_ifname; + struct pim_rpf *rpf = &up->rpf; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s", + src_str, + grp_str, + rpf_ifname, + rpf_addr_str, + rib_nexthop_str, + rpf->source_nexthop.mrib_route_metric, + rpf->source_nexthop.mrib_metric_preference, + VTY_NEWLINE); + } +} + +static void igmp_show_querier(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char query_hhmmss[10]; + char other_hhmmss[10]; + + pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); + pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer); + + vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s", + ifp->name, + inet_ntoa(igmp->ifaddr), + igmp->t_igmp_query_timer ? "THIS" : "OTHER", + igmp->startup_query_count, + query_hhmmss, + other_hhmmss, + VTY_NEWLINE); + } + } +} + +static void igmp_show_groups(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + char hhmmss[10]; + char uptime[10]; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer); + pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation); + + vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s", + ifp->name, + ifaddr_str, + group_str, + grp->group_filtermode_isexcl ? "EXCL" : "INCL", + hhmmss, + grp->group_source_list ? listcount(grp->group_source_list) : 0, + igmp_group_compat_mode(igmp, grp), + uptime, + VTY_NEWLINE); + + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_group_retransmission(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, "Interface Address Group RetTimer Counter RetSrcs%s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + char grp_retr_mmss[10]; + struct listnode *src_node; + struct igmp_source *src; + int grp_retr_sources = 0; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + pim_time_timer_to_mmss(grp_retr_mmss, sizeof(grp_retr_mmss), grp->t_group_query_retransmit_timer); + + + /* count group sources with retransmission state */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) { + if (src->source_query_retransmit_count > 0) { + ++grp_retr_sources; + } + } + + vty_out(vty, "%-9s %-15s %-15s %-8s %7d %7d%s", + ifp->name, + ifaddr_str, + group_str, + grp_retr_mmss, + grp->group_specific_query_retransmit_count, + grp_retr_sources, + VTY_NEWLINE); + + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_parameters(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "QRV: Robustness Variable SQI: Startup Query Interval%s" + "QQI: Query Interval OQPI: Other Querier Present Interval%s" + "QRI: Query Response Interval LMQT: Last Member Query Time%s" + "GMI: Group Membership Interval OHPI: Older Host Present Interval%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, + "Interface Address QRV QQI QRI GMI SQI OQPI LMQT OHPI %s", + VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + long gmi_dsec; /* Group Membership Interval */ + long oqpi_dsec; /* Other Querier Present Interval */ + int sqi; + long lmqt_dsec; + long ohpi_dsec; + long qri_dsec; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec) / 100; + + sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + + oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec) / 100; + + lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec, + igmp->querier_robustness_variable) / 100; + + ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + qri_dsec = pim_ifp->igmp_query_max_response_time_dsec; + + vty_out(vty, + "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s", + ifp->name, + ifaddr_str, + igmp->querier_robustness_variable, + igmp->querier_query_interval, + qri_dsec / 10, qri_dsec % 10, + gmi_dsec / 10, gmi_dsec % 10, + sqi, + oqpi_dsec / 10, oqpi_dsec % 10, + lmqt_dsec / 10, lmqt_dsec % 10, + ohpi_dsec / 10, ohpi_dsec % 10, + VTY_NEWLINE); + + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_sources(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group Source Timer Fwd Uptime %s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + char source_str[100]; + char mmss[10]; + char uptime[10]; + + pim_inet4_dump("", src->source_addr, source_str, sizeof(source_str)); + + pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer); + + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); + + vty_out(vty, "%-9s %-15s %-15s %-15s %5s %3s %8s%s", + ifp->name, + ifaddr_str, + group_str, + source_str, + mmss, + IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" : "N", + uptime, + VTY_NEWLINE); + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_source_retransmission(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, "Interface Address Group Source Counter%s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + char source_str[100]; + + pim_inet4_dump("", src->source_addr, source_str, sizeof(source_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %7d%s", + ifp->name, + ifaddr_str, + group_str, + source_str, + src->source_query_retransmit_count, + VTY_NEWLINE); + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void clear_igmp_interfaces() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_if_addr_del_all_igmp(ifp); + } + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_if_addr_add_all(ifp); + } +} + +static void clear_pim_interfaces() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (ifp->info) { + pim_neighbor_delete_all(ifp, "interface cleared"); + } + } +} + +static void clear_interfaces() +{ + clear_igmp_interfaces(); + clear_pim_interfaces(); +} + +DEFUN (pim_interface, + pim_interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + struct interface *ifp; + const char *ifname = argv[0]; + size_t sl; + + sl = strlen(ifname); + if (sl > INTERFACE_NAMSIZ) { + vty_out(vty, "%% Interface name %s is invalid: length exceeds " + "%d characters%s", + ifname, INTERFACE_NAMSIZ, VTY_NEWLINE); + return CMD_WARNING; + } + + ifp = if_lookup_by_name_len(ifname, sl); + if (!ifp) { + vty_out(vty, "%% Interface %s does not exist%s", ifname, VTY_NEWLINE); + + /* Returning here would prevent pimd from booting when there are + interface commands in pimd.conf, since all interfaces are + unknown at pimd boot time (the zebra daemon has not been + contacted for interface discovery). */ + + ifp = if_get_by_name_len(ifname, sl); + if (!ifp) { + vty_out(vty, "%% Could not create interface %s%s", ifname, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty->index = ifp; + vty->node = INTERFACE_NODE; + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_interfaces, + clear_ip_interfaces_cmd, + "clear ip interfaces", + CLEAR_STR + IP_STR + "Reset interfaces\n") +{ + clear_interfaces(); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_igmp_interfaces, + clear_ip_igmp_interfaces_cmd, + "clear ip igmp interfaces", + CLEAR_STR + IP_STR + CLEAR_IP_IGMP_STR + "Reset IGMP interfaces\n") +{ + clear_igmp_interfaces(); + + return CMD_SUCCESS; +} + +static void mroute_add_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_add(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +static void mroute_del_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_del(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +DEFUN (clear_ip_mroute, + clear_ip_mroute_cmd, + "clear ip mroute", + CLEAR_STR + IP_STR + "Reset multicast routes\n") +{ + mroute_del_all(); + mroute_add_all(); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_pim_interfaces, + clear_ip_pim_interfaces_cmd, + "clear ip pim interfaces", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + "Reset PIM interfaces\n") +{ + clear_pim_interfaces(); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_pim_oil, + clear_ip_pim_oil_cmd, + "clear ip pim oil", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + "Rescan PIM OIL (output interface list)\n") +{ + pim_scan_oil(); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_interface, + show_ip_igmp_interface_cmd, + "show ip igmp interface", + SHOW_STR + IP_STR + IGMP_STR + "IGMP interface information\n") +{ + igmp_show_interfaces(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_join, + show_ip_igmp_join_cmd, + "show ip igmp join", + SHOW_STR + IP_STR + IGMP_STR + "IGMP static join information\n") +{ + igmp_show_interface_join(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_groups, + show_ip_igmp_groups_cmd, + "show ip igmp groups", + SHOW_STR + IP_STR + IGMP_STR + IGMP_GROUP_STR) +{ + igmp_show_groups(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_groups_retransmissions, + show_ip_igmp_groups_retransmissions_cmd, + "show ip igmp groups retransmissions", + SHOW_STR + IP_STR + IGMP_STR + IGMP_GROUP_STR + "IGMP group retransmissions\n") +{ + igmp_show_group_retransmission(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_parameters, + show_ip_igmp_parameters_cmd, + "show ip igmp parameters", + SHOW_STR + IP_STR + IGMP_STR + "IGMP parameters information\n") +{ + igmp_show_parameters(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_sources, + show_ip_igmp_sources_cmd, + "show ip igmp sources", + SHOW_STR + IP_STR + IGMP_STR + IGMP_SOURCE_STR) +{ + igmp_show_sources(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_sources_retransmissions, + show_ip_igmp_sources_retransmissions_cmd, + "show ip igmp sources retransmissions", + SHOW_STR + IP_STR + IGMP_STR + IGMP_SOURCE_STR + "IGMP source retransmissions\n") +{ + igmp_show_source_retransmission(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_querier, + show_ip_igmp_querier_cmd, + "show ip igmp querier", + SHOW_STR + IP_STR + IGMP_STR + "IGMP querier information\n") +{ + igmp_show_querier(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_address, + show_ip_pim_address_cmd, + "show ip pim address", + SHOW_STR + IP_STR + PIM_STR + "PIM interface address\n") +{ + show_interface_address(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert, + show_ip_pim_assert_cmd, + "show ip pim assert", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert\n") +{ + pim_show_assert(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_internal, + show_ip_pim_assert_internal_cmd, + "show ip pim assert-internal", + SHOW_STR + IP_STR + PIM_STR + "PIM interface internal assert state\n") +{ + pim_show_assert_internal(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_metric, + show_ip_pim_assert_metric_cmd, + "show ip pim assert-metric", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert metric\n") +{ + pim_show_assert_metric(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_winner_metric, + show_ip_pim_assert_winner_metric_cmd, + "show ip pim assert-winner-metric", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert winner metric\n") +{ + pim_show_assert_winner_metric(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_dr, + show_ip_pim_dr_cmd, + "show ip pim designated-router", + SHOW_STR + IP_STR + PIM_STR + "PIM interface designated router\n") +{ + pim_show_dr(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_hello, + show_ip_pim_hello_cmd, + "show ip pim hello", + SHOW_STR + IP_STR + PIM_STR + "PIM interface hello information\n") +{ + pim_show_hello(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_interface, + show_ip_pim_interface_cmd, + "show ip pim interface", + SHOW_STR + IP_STR + PIM_STR + "PIM interface information\n") +{ + pim_show_interfaces(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_join, + show_ip_pim_join_cmd, + "show ip pim join", + SHOW_STR + IP_STR + PIM_STR + "PIM interface join information\n") +{ + pim_show_join(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_lan_prune_delay, + show_ip_pim_lan_prune_delay_cmd, + "show ip pim lan-prune-delay", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbors LAN prune delay parameters\n") +{ + pim_show_lan_prune_delay(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_local_membership, + show_ip_pim_local_membership_cmd, + "show ip pim local-membership", + SHOW_STR + IP_STR + PIM_STR + "PIM interface local-membership\n") +{ + pim_show_membership(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_jp_override_interval, + show_ip_pim_jp_override_interval_cmd, + "show ip pim jp-override-interval", + SHOW_STR + IP_STR + PIM_STR + "PIM interface J/P override interval\n") +{ + pim_show_jp_override_interval(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_neighbor, + show_ip_pim_neighbor_cmd, + "show ip pim neighbor", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbor information\n") +{ + pim_show_neighbors(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_secondary, + show_ip_pim_secondary_cmd, + "show ip pim secondary", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbor addresses\n") +{ + pim_show_neighbors_secondary(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream, + show_ip_pim_upstream_cmd, + "show ip pim upstream", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream information\n") +{ + pim_show_upstream(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream_join_desired, + show_ip_pim_upstream_join_desired_cmd, + "show ip pim upstream-join-desired", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream join-desired\n") +{ + pim_show_join_desired(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream_rpf, + show_ip_pim_upstream_rpf_cmd, + "show ip pim upstream-rpf", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream source rpf\n") +{ + pim_show_upstream_rpf(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_rpf, + show_ip_pim_rpf_cmd, + "show ip pim rpf", + SHOW_STR + IP_STR + PIM_STR + "PIM cached source rpf information\n") +{ + pim_show_rpf(vty); + + return CMD_SUCCESS; +} + +static void show_multicast_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct sioc_vif_req vreq; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + memset(&vreq, 0, sizeof(vreq)); + vreq.vifi = pim_ifp->mroute_vif_index; + + if (ioctl(qpim_mroute_socket_fd, SIOCGETVIFCNT, &vreq)) { + int e = errno; + vty_out(vty, + "ioctl(SIOCGETVIFCNT=%d) failure for interface %s vif_index=%d: errno=%d: %s%s", + SIOCGETVIFCNT, + ifp->name, + pim_ifp->mroute_vif_index, + e, + safe_strerror(e), + VTY_NEWLINE); + continue; + } + + ifaddr = pim_ifp->primary_address; + + vty_out(vty, "%-9s %-15s %3d %3d %7lu %7lu %10lu %10lu%s", + ifp->name, + inet_ntoa(ifaddr), + ifp->ifindex, + pim_ifp->mroute_vif_index, + vreq.icount, + vreq.ocount, + vreq.ibytes, + vreq.obytes, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_multicast, + show_ip_multicast_cmd, + "show ip multicast", + SHOW_STR + IP_STR + "Multicast global information\n") +{ + time_t now = pim_time_monotonic_sec(); + + if (PIM_MROUTE_IS_ENABLED) { + char uptime[10]; + + vty_out(vty, "Mroute socket descriptor: %d%s", + qpim_mroute_socket_fd, + VTY_NEWLINE); + + pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation); + vty_out(vty, "Mroute socket uptime: %s%s", + uptime, + VTY_NEWLINE); + } + else { + vty_out(vty, "Multicast disabled%s", + VTY_NEWLINE); + } + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Zclient update socket: "); + if (qpim_zclient_update) { + vty_out(vty, "%d failures=%d%s", qpim_zclient_update->sock, + qpim_zclient_update->fail, VTY_NEWLINE); + } + else { + vty_out(vty, "%s", VTY_NEWLINE); + } + vty_out(vty, "Zclient lookup socket: "); + if (qpim_zclient_lookup) { + vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock, + qpim_zclient_lookup->fail, VTY_NEWLINE); + } + else { + vty_out(vty, "%s", VTY_NEWLINE); + } + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Current highest VifIndex: %d%s", + qpim_mroute_oif_highest_vif_index, + VTY_NEWLINE); + vty_out(vty, "Maximum highest VifIndex: %d%s", + MAXVIFS - 1, + VTY_NEWLINE); + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Upstream Join Timer: %d secs%s", + qpim_t_periodic, + VTY_NEWLINE); + vty_out(vty, "Join/Prune Holdtime: %d secs%s", + PIM_JP_HOLDTIME, + VTY_NEWLINE); + + vty_out(vty, "%s", VTY_NEWLINE); + + show_rpf_refresh_stats(vty, now); + + vty_out(vty, "%s", VTY_NEWLINE); + + show_scan_oil_stats(vty, now); + + show_multicast_interfaces(vty); + + return CMD_SUCCESS; +} + +static void show_mroute(struct vty *vty) +{ + struct listnode *node; + struct channel_oil *c_oil; + time_t now; + + vty_out(vty, "Proto: I=IGMP P=PIM%s%s", VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Source Group Proto Input iVifI Output oVifI TTL Uptime %s", + VTY_NEWLINE); + + now = pim_time_monotonic_sec(); + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + char group_str[100]; + char source_str[100]; + int oif_vif_index; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + + for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { + struct interface *ifp_in; + struct interface *ifp_out; + char oif_uptime[10]; + int ttl; + char proto[5]; + + ttl = c_oil->oil.mfcc_ttls[oif_vif_index]; + if (ttl < 1) + continue; + + ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + ifp_out = pim_if_find_by_vif_index(oif_vif_index); + + pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]); + + proto[0] = '\0'; + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { + strcat(proto, "P"); + } + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { + strcat(proto, "I"); + } + + vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", + source_str, + group_str, + proto, + ifp_in ? ifp_in->name : "", + c_oil->oil.mfcc_parent, + ifp_out ? ifp_out->name : "", + oif_vif_index, + ttl, + oif_uptime, + VTY_NEWLINE); + } + } +} + +DEFUN (show_ip_mroute, + show_ip_mroute_cmd, + "show ip mroute", + SHOW_STR + IP_STR + MROUTE_STR) +{ + show_mroute(vty); + return CMD_SUCCESS; +} + +static void show_mroute_count(struct vty *vty) +{ + struct listnode *node; + struct channel_oil *c_oil; + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Source Group Packets Bytes WrongIf %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + char group_str[100]; + char source_str[100]; + struct sioc_sg_req sgreq; + + memset(&sgreq, 0, sizeof(sgreq)); + sgreq.src = c_oil->oil.mfcc_origin; + sgreq.grp = c_oil->oil.mfcc_mcastgrp; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + + if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) { + int e = errno; + vty_out(vty, + "ioctl(SIOCGETSGCNT=%d) failure for (S,G)=(%s,%s): errno=%d: %s%s", + SIOCGETSGCNT, + source_str, + group_str, + e, + safe_strerror(e), + VTY_NEWLINE); + continue; + } + + vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s", + source_str, + group_str, + sgreq.pktcnt, + sgreq.bytecnt, + sgreq.wrong_if, + VTY_NEWLINE); + + } +} + +DEFUN (show_ip_mroute_count, + show_ip_mroute_count_cmd, + "show ip mroute count", + SHOW_STR + IP_STR + MROUTE_STR + "Route and packet count data\n") +{ + show_mroute_count(vty); + return CMD_SUCCESS; +} + +DEFUN (show_ip_rib, + show_ip_rib_cmd, + "show ip rib A.B.C.D", + SHOW_STR + IP_STR + RIB_STR + "Unicast address\n") +{ + struct in_addr addr; + const char *addr_str; + struct pim_nexthop nexthop; + char nexthop_addr_str[100]; + int result; + + addr_str = argv[0]; + result = inet_pton(AF_INET, addr_str, &addr); + if (result <= 0) { + vty_out(vty, "Bad unicast address %s: errno=%d: %s%s", + addr_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + if (pim_nexthop_lookup(&nexthop, addr)) { + vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s", + addr_str, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "Address NextHop Interface Metric Preference%s", + VTY_NEWLINE); + + pim_inet4_dump("", nexthop.mrib_nexthop_addr, + nexthop_addr_str, sizeof(nexthop_addr_str)); + + vty_out(vty, "%-15s %-15s %-9s %6d %10d%s", + addr_str, + nexthop_addr_str, + nexthop.interface ? nexthop.interface->name : "", + nexthop.mrib_route_metric, + nexthop.mrib_metric_preference, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void show_ssmpingd(struct vty *vty) +{ + struct listnode *node; + struct ssmpingd_sock *ss; + time_t now; + + vty_out(vty, "Source Socket Address Port Uptime Requests%s", + VTY_NEWLINE); + + if (!qpim_ssmpingd_list) + return; + + now = pim_time_monotonic_sec(); + + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { + char source_str[100]; + char ss_uptime[10]; + struct sockaddr_in bind_addr; + socklen_t len = sizeof(bind_addr); + char bind_addr_str[100]; + + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + + if (pim_socket_getsockname(ss->sock_fd, (struct sockaddr *) &bind_addr, &len)) { + vty_out(vty, "%% Failure reading socket name for ssmpingd source %s on fd=%d%s", + source_str, ss->sock_fd, VTY_NEWLINE); + } + + pim_inet4_dump("", bind_addr.sin_addr, bind_addr_str, sizeof(bind_addr_str)); + pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation); + + vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld%s", + source_str, + ss->sock_fd, + bind_addr_str, + ntohs(bind_addr.sin_port), + ss_uptime, + (long long)ss->requests, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_ssmpingd, + show_ip_ssmpingd_cmd, + "show ip ssmpingd", + SHOW_STR + IP_STR + SHOW_SSMPINGD_STR) +{ + show_ssmpingd(vty); + return CMD_SUCCESS; +} + +DEFUN (ip_multicast_routing, + ip_multicast_routing_cmd, + PIM_CMD_IP_MULTICAST_ROUTING, + IP_STR + "Enable IP multicast forwarding\n") +{ + pim_mroute_socket_enable(); + pim_if_add_vif_all(); + mroute_add_all(); + return CMD_SUCCESS; +} + +DEFUN (no_ip_multicast_routing, + no_ip_multicast_routing_cmd, + PIM_CMD_NO " " PIM_CMD_IP_MULTICAST_ROUTING, + NO_STR + IP_STR + "Global IP configuration subcommands\n" + "Enable IP multicast forwarding\n") +{ + mroute_del_all(); + pim_if_del_vif_all(); + pim_mroute_socket_disable(); + return CMD_SUCCESS; +} + +DEFUN (ip_ssmpingd, + ip_ssmpingd_cmd, + "ip ssmpingd [A.B.C.D]", + IP_STR + CONF_SSMPINGD_STR + "Source address\n") +{ + int result; + struct in_addr source_addr; + const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0"; + + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_ssmpingd_start(source_addr); + if (result) { + vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s", + source_str, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ssmpingd, + no_ip_ssmpingd_cmd, + "no ip ssmpingd [A.B.C.D]", + NO_STR + IP_STR + CONF_SSMPINGD_STR + "Source address\n") +{ + int result; + struct in_addr source_addr; + const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0"; + + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_ssmpingd_stop(source_addr); + if (result) { + vty_out(vty, "%% Failure stopping ssmpingd for source %s: %d%s", + source_str, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_igmp, + interface_ip_igmp_cmd, + "ip igmp", + IP_STR + IFACE_IGMP_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + pim_ifp = pim_if_new(ifp, 1 /* igmp=true */, 0 /* pim=false */); + if (!pim_ifp) { + vty_out(vty, "Could not enable IGMP on interface %s%s", + ifp->name, VTY_NEWLINE); + return CMD_WARNING; + } + } + else { + PIM_IF_DO_IGMP(pim_ifp->options); + } + + pim_if_addr_add_all(ifp); + pim_if_membership_refresh(ifp); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp, + interface_no_ip_igmp_cmd, + "no ip igmp", + NO_STR + IP_STR + IFACE_IGMP_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + if (!pim_ifp) + return CMD_SUCCESS; + + PIM_IF_DONT_IGMP(pim_ifp->options); + + pim_if_membership_clear(ifp); + + pim_if_addr_del_all_igmp(ifp); + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + pim_if_delete(ifp); + } + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_igmp_join, + interface_ip_igmp_join_cmd, + "ip igmp join A.B.C.D A.B.C.D", + IP_STR + IFACE_IGMP_STR + "IGMP join multicast group\n" + "Multicast group address\n" + "Source address\n") +{ + struct interface *ifp; + const char *group_str; + const char *source_str; + struct in_addr group_addr; + struct in_addr source_addr; + int result; + + ifp = vty->index; + + /* Group address */ + group_str = argv[0]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[1]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_if_igmp_join_add(ifp, group_addr, source_addr); + if (result) { + vty_out(vty, "%% Failure joining IGMP group %s source %s on interface %s: %d%s", + group_str, source_str, ifp->name, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_join, + interface_no_ip_igmp_join_cmd, + "no ip igmp join A.B.C.D A.B.C.D", + NO_STR + IP_STR + IFACE_IGMP_STR + "IGMP join multicast group\n" + "Multicast group address\n" + "Source address\n") +{ + struct interface *ifp; + const char *group_str; + const char *source_str; + struct in_addr group_addr; + struct in_addr source_addr; + int result; + + ifp = vty->index; + + /* Group address */ + group_str = argv[0]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[1]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_if_igmp_join_del(ifp, group_addr, source_addr); + if (result) { + vty_out(vty, "%% Failure leaving IGMP group %s source %s on interface %s: %d%s", + group_str, source_str, ifp->name, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* + CLI reconfiguration affects the interface level (struct pim_interface). + This function propagates the reconfiguration to every active socket + for that interface. + */ +static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + zassert(igmp); + + /* other querier present? */ + + if (igmp->t_other_querier_timer) + return; + + /* this is the querier */ + + zassert(igmp->interface); + zassert(igmp->interface->info); + + ifp = igmp->interface; + pim_ifp = ifp->info; + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: Querier %s on %s reconfig query_interval=%d", + __PRETTY_FUNCTION__, + ifaddr_str, + ifp->name, + pim_ifp->igmp_default_query_interval); + } + + /* + igmp_startup_mode_on() will reset QQI: + + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; + */ + igmp_startup_mode_on(igmp); +} + +static void igmp_sock_query_reschedule(struct igmp_sock *igmp) +{ + if (igmp->t_igmp_query_timer) { + /* other querier present */ + zassert(igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + + pim_igmp_general_query_off(igmp); + pim_igmp_general_query_on(igmp); + + zassert(igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + } + else { + /* this is the querier */ + + zassert(!igmp->t_igmp_query_timer); + zassert(igmp->t_other_querier_timer); + + pim_igmp_other_querier_timer_off(igmp); + pim_igmp_other_querier_timer_on(igmp); + + zassert(!igmp->t_igmp_query_timer); + zassert(igmp->t_other_querier_timer); + } +} + +static void change_query_interval(struct pim_interface *pim_ifp, + int query_interval) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp->igmp_default_query_interval = query_interval; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + igmp_sock_query_interval_reconfig(igmp); + igmp_sock_query_reschedule(igmp); + } +} + +static void change_query_max_response_time(struct pim_interface *pim_ifp, + int query_max_response_time_dsec) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp->igmp_query_max_response_time_dsec = query_max_response_time_dsec; + + /* + Below we modify socket/group/source timers in order to quickly + reflect the change. Otherwise, those timers would eventually catch + up. + */ + + /* scan all sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + struct listnode *grp_node; + struct igmp_group *grp; + + /* reschedule socket general query */ + igmp_sock_query_reschedule(igmp); + + /* scan socket groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, grp)) { + struct listnode *src_node; + struct igmp_source *src; + + /* reset group timers for groups in EXCLUDE mode */ + if (grp->group_filtermode_isexcl) { + igmp_group_reset_gmi(grp); + } + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) { + + /* reset source timers for sources with running timers */ + if (src->t_source_timer) { + igmp_source_reset_gmi(igmp, grp, src); + } + } + } + } +} + +#define IGMP_QUERY_INTERVAL_MIN (1) +#define IGMP_QUERY_INTERVAL_MAX (1800) + +DEFUN (interface_ip_igmp_query_interval, + interface_ip_igmp_query_interval_cmd, + PIM_CMD_IP_IGMP_QUERY_INTERVAL " <1-1800>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_INTERVAL_STR + "Query interval in seconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_interval; + int query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_interval = atoi(argv[0]); + query_interval_dsec = 10 * query_interval; + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_interval < IGMP_QUERY_INTERVAL_MIN) { + vty_out(vty, "General query interval %d lower than minimum %d%s", + query_interval, + IGMP_QUERY_INTERVAL_MIN, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_interval > IGMP_QUERY_INTERVAL_MAX) { + vty_out(vty, "General query interval %d higher than maximum %d%s", + query_interval, + IGMP_QUERY_INTERVAL_MAX, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) { + vty_out(vty, + "Can't set general query interval %d dsec <= query max response time %d dsec.%s", + query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_interval(pim_ifp, query_interval); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_interval, + interface_no_ip_igmp_query_interval_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_INTERVAL, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_INTERVAL_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = IGMP_GENERAL_QUERY_INTERVAL * 10; + + if (default_query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) { + vty_out(vty, + "Can't set default general query interval %d dsec <= query max response time %d dsec.%s", + default_query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_interval(pim_ifp, IGMP_GENERAL_QUERY_INTERVAL); + + return CMD_SUCCESS; +} + +#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1) +#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25) + +DEFUN (interface_ip_igmp_query_max_response_time, + interface_ip_igmp_query_max_response_time_cmd, + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME " <1-25>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR + "Query response value in seconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_max_response_time; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_max_response_time = atoi(argv[0]); + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) { + vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s", + query_max_response_time, + IGMP_QUERY_MAX_RESPONSE_TIME_MIN, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) { + vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s", + query_max_response_time, + IGMP_QUERY_MAX_RESPONSE_TIME_MAX, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (query_max_response_time >= pim_ifp->igmp_default_query_interval) { + vty_out(vty, + "Can't set query max response time %d sec >= general query interval %d sec%s", + query_max_response_time, pim_ifp->igmp_default_query_interval, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, 10 * query_max_response_time); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_max_response_time, + interface_no_ip_igmp_query_max_response_time_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { + vty_out(vty, + "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", + IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + + return CMD_SUCCESS; +} + +#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10) +#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250) + +DEFUN (interface_ip_igmp_query_max_response_time_dsec, + interface_ip_igmp_query_max_response_time_dsec_cmd, + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC " <10-250>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR + "Query response value in deciseconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_max_response_time_dsec; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_max_response_time_dsec = atoi(argv[0]); + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) { + vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s", + query_max_response_time_dsec, + IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) { + vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s", + query_max_response_time_dsec, + IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC, + VTY_NEWLINE); + return CMD_WARNING; + } + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (query_max_response_time_dsec >= default_query_interval_dsec) { + vty_out(vty, + "Can't set query max response time %d dsec >= general query interval %d dsec%s", + query_max_response_time_dsec, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, query_max_response_time_dsec); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_max_response_time_dsec, + interface_no_ip_igmp_query_max_response_time_dsec_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { + vty_out(vty, + "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", + IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_pim_ssm, + interface_ip_pim_ssm_cmd, + "ip pim ssm", + IP_STR + PIM_STR + IFACE_PIM_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */); + if (!pim_ifp) { + vty_out(vty, "Could not enable PIM on interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + else { + PIM_IF_DO_PIM(pim_ifp->options); + } + + pim_if_addr_add_all(ifp); + pim_if_membership_refresh(ifp); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_pim_ssm, + interface_no_ip_pim_ssm_cmd, + "no ip pim ssm", + NO_STR + IP_STR + PIM_STR + IFACE_PIM_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + if (!pim_ifp) + return CMD_SUCCESS; + + PIM_IF_DONT_PIM(pim_ifp->options); + + pim_if_membership_clear(ifp); + + /* + pim_if_addr_del_all() removes all sockets from + pim_ifp->igmp_socket_list. + */ + pim_if_addr_del_all(ifp); + + /* + pim_sock_delete() removes all neighbors from + pim_ifp->pim_neighbor_list. + */ + pim_sock_delete(ifp, "pim unconfigured on interface"); + + if (!PIM_IF_TEST_IGMP(pim_ifp->options)) { + pim_if_delete(ifp); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_igmp, + debug_igmp_cmd, + "debug igmp", + DEBUG_STR + DEBUG_IGMP_STR) +{ + PIM_DO_DEBUG_IGMP_EVENTS; + PIM_DO_DEBUG_IGMP_PACKETS; + PIM_DO_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp, + no_debug_igmp_cmd, + "no debug igmp", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR) +{ + PIM_DONT_DEBUG_IGMP_EVENTS; + PIM_DONT_DEBUG_IGMP_PACKETS; + PIM_DONT_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp, + undebug_igmp_cmd, + "undebug igmp", + UNDEBUG_STR + DEBUG_IGMP_STR) + +DEFUN (debug_igmp_events, + debug_igmp_events_cmd, + "debug igmp events", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) +{ + PIM_DO_DEBUG_IGMP_EVENTS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_events, + no_debug_igmp_events_cmd, + "no debug igmp events", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) +{ + PIM_DONT_DEBUG_IGMP_EVENTS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_events, + undebug_igmp_events_cmd, + "undebug igmp events", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) + +DEFUN (debug_igmp_packets, + debug_igmp_packets_cmd, + "debug igmp packets", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) +{ + PIM_DO_DEBUG_IGMP_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_packets, + no_debug_igmp_packets_cmd, + "no debug igmp packets", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) +{ + PIM_DONT_DEBUG_IGMP_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_packets, + undebug_igmp_packets_cmd, + "undebug igmp packets", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) + +DEFUN (debug_igmp_trace, + debug_igmp_trace_cmd, + "debug igmp trace", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) +{ + PIM_DO_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_trace, + no_debug_igmp_trace_cmd, + "no debug igmp trace", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) +{ + PIM_DONT_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_trace, + undebug_igmp_trace_cmd, + "undebug igmp trace", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) + +DEFUN (debug_mroute, + debug_mroute_cmd, + "debug mroute", + DEBUG_STR + DEBUG_MROUTE_STR) +{ + PIM_DO_DEBUG_MROUTE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_mroute, + no_debug_mroute_cmd, + "no debug mroute", + NO_STR + DEBUG_STR + DEBUG_MROUTE_STR) +{ + PIM_DONT_DEBUG_MROUTE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_mroute, + undebug_mroute_cmd, + "undebug mroute", + UNDEBUG_STR + DEBUG_MROUTE_STR) + +DEFUN (debug_pim, + debug_pim_cmd, + "debug pim", + DEBUG_STR + DEBUG_PIM_STR) +{ + PIM_DO_DEBUG_PIM_EVENTS; + PIM_DO_DEBUG_PIM_PACKETS; + PIM_DO_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim, + no_debug_pim_cmd, + "no debug pim", + NO_STR + DEBUG_STR + DEBUG_PIM_STR) +{ + PIM_DONT_DEBUG_PIM_EVENTS; + PIM_DONT_DEBUG_PIM_PACKETS; + PIM_DONT_DEBUG_PIM_TRACE; + + PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; + PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim, + undebug_pim_cmd, + "undebug pim", + UNDEBUG_STR + DEBUG_PIM_STR) + +DEFUN (debug_pim_events, + debug_pim_events_cmd, + "debug pim events", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) +{ + PIM_DO_DEBUG_PIM_EVENTS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_events, + no_debug_pim_events_cmd, + "no debug pim events", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) +{ + PIM_DONT_DEBUG_PIM_EVENTS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_events, + undebug_pim_events_cmd, + "undebug pim events", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) + +DEFUN (debug_pim_packets, + debug_pim_packets_cmd, + "debug pim packets", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR) +{ + PIM_DO_DEBUG_PIM_PACKETS; + vty_out (vty, "PIM Packet debugging is on %s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (debug_pim_packets_filter, + debug_pim_packets_filter_cmd, + "debug pim packets (hello|joins)", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) +{ + if (strncmp(argv[0],"h",1) == 0) + { + PIM_DO_DEBUG_PIM_HELLO; + vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE); + } + else if (strncmp(argv[0],"j",1) == 0) + { + PIM_DO_DEBUG_PIM_J_P; + vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packets, + no_debug_pim_packets_cmd, + "no debug pim packets", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETS; + vty_out (vty, "PIM Packet debugging is off %s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packets_filter, + no_debug_pim_packets_filter_cmd, + "no debug pim packets (hello|joins)", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) +{ + if (strncmp(argv[0],"h",1) == 0) + { + PIM_DONT_DEBUG_PIM_HELLO; + vty_out (vty, "PIM Hello debugging is off %s", VTY_NEWLINE); + } + else if (strncmp(argv[0],"j",1) == 0) + { + PIM_DONT_DEBUG_PIM_J_P; + vty_out (vty, "PIM Join/Prune debugging is off %s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packets, + undebug_pim_packets_cmd, + "undebug pim packets", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR) + +DEFUN (debug_pim_packetdump_send, + debug_pim_packetdump_send_cmd, + "debug pim packet-dump send", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) +{ + PIM_DO_DEBUG_PIM_PACKETDUMP_SEND; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packetdump_send, + no_debug_pim_packetdump_send_cmd, + "no debug pim packet-dump send", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packetdump_send, + undebug_pim_packetdump_send_cmd, + "undebug pim packet-dump send", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) + +DEFUN (debug_pim_packetdump_recv, + debug_pim_packetdump_recv_cmd, + "debug pim packet-dump receive", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) +{ + PIM_DO_DEBUG_PIM_PACKETDUMP_RECV; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packetdump_recv, + no_debug_pim_packetdump_recv_cmd, + "no debug pim packet-dump receive", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packetdump_recv, + undebug_pim_packetdump_recv_cmd, + "undebug pim packet-dump receive", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) + +DEFUN (debug_pim_trace, + debug_pim_trace_cmd, + "debug pim trace", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) +{ + PIM_DO_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_trace, + no_debug_pim_trace_cmd, + "no debug pim trace", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) +{ + PIM_DONT_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_trace, + undebug_pim_trace_cmd, + "undebug pim trace", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) + +DEFUN (debug_ssmpingd, + debug_ssmpingd_cmd, + "debug ssmpingd", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) +{ + PIM_DO_DEBUG_SSMPINGD; + return CMD_SUCCESS; +} + +DEFUN (no_debug_ssmpingd, + no_debug_ssmpingd_cmd, + "no debug ssmpingd", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) +{ + PIM_DONT_DEBUG_SSMPINGD; + return CMD_SUCCESS; +} + +ALIAS (no_debug_ssmpingd, + undebug_ssmpingd_cmd, + "undebug ssmpingd", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) + +DEFUN (debug_pim_zebra, + debug_pim_zebra_cmd, + "debug pim zebra", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) +{ + PIM_DO_DEBUG_ZEBRA; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_zebra, + no_debug_pim_zebra_cmd, + "no debug pim zebra", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) +{ + PIM_DONT_DEBUG_ZEBRA; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_zebra, + undebug_pim_zebra_cmd, + "undebug pim zebra", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) + +DEFUN (show_debugging, + show_debugging_cmd, + "show debugging", + SHOW_STR + "State of each debugging option\n") +{ + pim_debug_config_write(vty); + return CMD_SUCCESS; +} + +static struct igmp_sock *find_igmp_sock_by_fd(int fd) +{ + struct listnode *ifnode; + struct interface *ifp; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + + if (!ifp->info) + continue; + + pim_ifp = ifp->info; + + /* lookup igmp socket under current interface */ + igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd); + if (igmp) + return igmp; + } + + return 0; +} + +DEFUN (test_igmp_receive_report, + test_igmp_receive_report_cmd, + "test igmp receive report <0-65535> A.B.C.D <1-6> .LINE", + "Test\n" + "Test IGMP protocol\n" + "Test IGMP message\n" + "Test IGMP report\n" + "Socket\n" + "IGMP group address\n" + "Record type\n" + "Sources\n") +{ + char buf[1000]; + char *igmp_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int igmp_msg_len; + const char *socket; + int socket_fd; + const char *grp_str; + struct in_addr grp_addr; + const char *record_type_str; + int record_type; + const char *src_str; + int result; + struct igmp_sock *igmp; + char *group_record; + int num_sources; + struct in_addr *sources; + struct in_addr *src_addr; + int argi; + + socket = argv[0]; + socket_fd = atoi(socket); + igmp = find_igmp_sock_by_fd(socket_fd); + if (!igmp) { + vty_out(vty, "Could not find IGMP socket %s: fd=%d%s", + socket, socket_fd, VTY_NEWLINE); + return CMD_WARNING; + } + + grp_str = argv[1]; + result = inet_pton(AF_INET, grp_str, &grp_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + grp_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + record_type_str = argv[2]; + record_type = atoi(record_type_str); + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_IGMP; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = igmp->ifaddr; + ip_hdr->ip_dst = igmp->ifaddr; + + /* + Build IGMP v3 report message + */ + igmp_msg = buf + ip_hlen; + group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; + *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */ + *(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type; + memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr)); + + /* Scan LINE sources */ + sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET); + src_addr = sources; + for (argi = 3; argi < argc; ++argi,++src_addr) { + src_str = argv[argi]; + result = inet_pton(AF_INET, src_str, src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + src_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + } + num_sources = src_addr - sources; + + *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources); + + igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */ + + /* compute checksum */ + *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len); + + /* "receive" message */ + + ip_msg_len = ip_hlen + igmp_msg_len; + result = pim_igmp_packet(igmp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int hexval(uint8_t ch) +{ + return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a'); +} + +DEFUN (test_pim_receive_dump, + test_pim_receive_dump_cmd, + "test pim receive dump INTERFACE A.B.C.D .LINE", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM packet dump reception from neighbor\n" + "Interface\n" + "Neighbor address\n" + "Packet dump\n") +{ + uint8_t buf[1000]; + uint8_t *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *ifname; + struct interface *ifp; + int argi; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM hello message + */ + pim_msg = buf + ip_hlen; + pim_msg_size = 0; + + /* Scan LINE dump into buffer */ + for (argi = 2; argi < argc; ++argi) { + const char *str = argv[argi]; + int str_len = strlen(str); + int str_last = str_len - 1; + int i; + + if (str_len % 2) { + vty_out(vty, "%% Uneven hex array arg %d=%s%s", + argi, str, VTY_NEWLINE); + return CMD_WARNING; + } + + for (i = 0; i < str_last; i += 2) { + uint8_t octet; + int left; + uint8_t h1 = str[i]; + uint8_t h2 = str[i + 1]; + + if (!isxdigit(h1) || !isxdigit(h2)) { + vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s", + h1, h2, argi, str, VTY_NEWLINE); + return CMD_WARNING; + } + octet = (hexval(h1) << 4) + hexval(h2); + + left = sizeof(buf) - ip_hlen - pim_msg_size; + if (left < 1) { + vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s", + sizeof(buf), left, argi, str, octet, VTY_NEWLINE); + return CMD_WARNING; + } + + pim_msg[pim_msg_size++] = octet; + } + } + + ip_msg_len = ip_hlen + pim_msg_size; + + vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s", + sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE); + + /* "receive" message */ + + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_hello, + test_pim_receive_hello_cmd, + "test pim receive hello INTERFACE A.B.C.D <0-65535> <0-65535> <0-65535> <0-32767> <0-65535> <0-1>[LINE]", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM hello reception from neighbor\n" + "Interface\n" + "Neighbor address\n" + "Neighbor holdtime\n" + "Neighbor DR priority\n" + "Neighbor generation ID\n" + "Neighbor propagation delay (msec)\n" + "Neighbor override interval (msec)\n" + "Neighbor LAN prune delay T-bit\n" + "Neighbor secondary addresses\n") +{ + uint8_t buf[1000]; + uint8_t *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_tlv_size; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *ifname; + struct interface *ifp; + uint16_t neigh_holdtime; + uint16_t neigh_propagation_delay; + uint16_t neigh_override_interval; + int neigh_can_disable_join_suppression; + uint32_t neigh_dr_priority; + uint32_t neigh_generation_id; + int argi; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + neigh_holdtime = atoi(argv[2]); + neigh_dr_priority = atoi(argv[3]); + neigh_generation_id = atoi(argv[4]); + neigh_propagation_delay = atoi(argv[5]); + neigh_override_interval = atoi(argv[6]); + neigh_can_disable_join_suppression = atoi(argv[7]); + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM hello message + */ + pim_msg = buf + ip_hlen; + + /* Scan LINE addresses */ + for (argi = 8; argi < argc; ++argi) { + const char *sec_str = argv[argi]; + struct in_addr sec_addr; + result = inet_pton(AF_INET, sec_str, &sec_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s", + sec_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, + "FIXME WRITEME consider neighbor secondary address %s%s", + sec_str, VTY_NEWLINE); + } + + pim_tlv_size = pim_hello_build_tlv(ifp->name, + pim_msg + PIM_PIM_MIN_LEN, + sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN, + neigh_holdtime, + neigh_dr_priority, + neigh_generation_id, + neigh_propagation_delay, + neigh_override_interval, + neigh_can_disable_join_suppression, + 0 /* FIXME secondary address list */); + if (pim_tlv_size < 0) { + vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s", + pim_tlv_size, VTY_NEWLINE); + return CMD_WARNING; + } + + pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_HELLO); + + /* "receive" message */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_assert, + test_pim_receive_assert_cmd, + "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D <0-65535> <0-65535> <0-1>", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test reception of PIM assert\n" + "Interface\n" + "Neighbor address\n" + "Assert multicast group address\n" + "Assert unicast source address\n" + "Assert metric preference\n" + "Assert route metric\n" + "Assert RPT bit flag\n") +{ + uint8_t buf[1000]; + uint8_t *buf_pastend = buf + sizeof(buf); + uint8_t *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *group_str; + struct in_addr group_addr; + const char *source_str; + struct in_addr source_addr; + const char *ifname; + struct interface *ifp; + uint32_t assert_metric_preference; + uint32_t assert_route_metric; + uint32_t assert_rpt_bit_flag; + int remain; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Group address */ + group_str = argv[2]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[3]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + assert_metric_preference = atoi(argv[4]); + assert_route_metric = atoi(argv[5]); + assert_rpt_bit_flag = atoi(argv[6]); + + remain = buf_pastend - buf; + if (remain < (int) sizeof(struct ip)) { + vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s", + remain, sizeof(struct ip), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM assert message + */ + pim_msg = buf + ip_hlen; /* skip ip header */ + + pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp, + group_addr, source_addr, + assert_metric_preference, + assert_route_metric, + assert_rpt_bit_flag); + if (pim_msg_size < 0) { + vty_out(vty, "Failure building PIM assert message: size=%d%s", + pim_msg_size, VTY_NEWLINE); + return CMD_WARNING; + } + + /* "receive" message */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int recv_joinprune(struct vty *vty, + const char *argv[], + int src_is_join) +{ + uint8_t buf[1000]; + const uint8_t *buf_pastend = buf + sizeof(buf); + uint8_t *pim_msg; + uint8_t *pim_msg_curr; + int pim_msg_size; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + uint16_t neigh_holdtime; + const char *neigh_dst_str; + struct in_addr neigh_dst_addr; + const char *neigh_src_str; + struct in_addr neigh_src_addr; + const char *group_str; + struct in_addr group_addr; + const char *source_str; + struct in_addr source_addr; + const char *ifname; + struct interface *ifp; + int result; + int remain; + uint16_t num_joined; + uint16_t num_pruned; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + neigh_holdtime = atoi(argv[1]); + + /* Neighbor destination address */ + neigh_dst_str = argv[2]; + result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s", + neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor source address */ + neigh_src_str = argv[3]; + result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s", + neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Multicast group address */ + group_str = argv[4]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Multicast source address */ + source_str = argv[5]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_src_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM message + */ + pim_msg = buf + ip_hlen; + + /* skip room for pim header */ + pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + neigh_dst_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding destination address %s: space left=%d%s", + neigh_dst_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + remain = buf_pastend - pim_msg_curr; + if (remain < 4) { + vty_out(vty, "Group will not fit: space left=%d%s", + remain, VTY_NEWLINE); + return CMD_WARNING; + } + + *pim_msg_curr = 0; /* reserved */ + ++pim_msg_curr; + *pim_msg_curr = 1; /* number of groups */ + ++pim_msg_curr; + *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding group address %s: space left=%d%s", + group_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + remain = buf_pastend - pim_msg_curr; + if (remain < 4) { + vty_out(vty, "Sources will not fit: space left=%d%s", + remain, VTY_NEWLINE); + return CMD_WARNING; + } + + if (src_is_join) { + num_joined = 1; + num_pruned = 0; + } + else { + num_joined = 0; + num_pruned = 1; + } + + /* number of joined sources */ + *((uint16_t *) pim_msg_curr) = htons(num_joined); + ++pim_msg_curr; + ++pim_msg_curr; + + /* number of pruned sources */ + *((uint16_t *) pim_msg_curr) = htons(num_pruned); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding source address %s: space left=%d%s", + source_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Add PIM header */ + + pim_msg_size = pim_msg_curr - pim_msg; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_JOIN_PRUNE); + + /* + "Receive" message + */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_join, + test_pim_receive_join_cmd, + "test pim receive join INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM join reception from neighbor\n" + "Interface\n" + "Neighbor holdtime\n" + "Upstream neighbor unicast destination address\n" + "Downstream neighbor unicast source address\n" + "Multicast group address\n" + "Unicast source address\n") +{ + return recv_joinprune(vty, argv, 1 /* src_is_join=true */); +} + +DEFUN (test_pim_receive_prune, + test_pim_receive_prune_cmd, + "test pim receive prune INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM prune reception from neighbor\n" + "Interface\n" + "Neighbor holdtime\n" + "Upstream neighbor unicast destination address\n" + "Downstream neighbor unicast source address\n" + "Multicast group address\n" + "Unicast source address\n") +{ + return recv_joinprune(vty, argv, 0 /* src_is_join=false */); +} + +DEFUN (test_pim_receive_upcall, + test_pim_receive_upcall_cmd, + "test pim receive upcall (nocache|wrongvif|wholepkt) <0-65535> A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test reception of kernel upcall\n" + "NOCACHE kernel upcall\n" + "WRONGVIF kernel upcall\n" + "WHOLEPKT kernel upcall\n" + "Input interface vif index\n" + "Multicast group address\n" + "Multicast source address\n") +{ + struct igmpmsg msg; + const char *upcall_type; + const char *group_str; + const char *source_str; + int result; + + upcall_type = argv[0]; + + if (upcall_type[0] == 'n') + msg.im_msgtype = IGMPMSG_NOCACHE; + else if (upcall_type[1] == 'r') + msg.im_msgtype = IGMPMSG_WRONGVIF; + else if (upcall_type[1] == 'h') + msg.im_msgtype = IGMPMSG_WHOLEPKT; + else { + vty_out(vty, "Unknown kernel upcall type: %s%s", + upcall_type, VTY_NEWLINE); + return CMD_WARNING; + } + + msg.im_vif = atoi(argv[1]); + + /* Group address */ + group_str = argv[2]; + result = inet_pton(AF_INET, group_str, &msg.im_dst); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[3]; + result = inet_pton(AF_INET, source_str, &msg.im_src); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + msg.im_mbz = 0; /* Must be zero */ + + result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg)); + if (result) { + vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s", + sizeof(msg), result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +void pim_cmd_init() +{ + install_node (&pim_global_node, pim_global_config_write); /* PIM_NODE */ + install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */ + + install_element (CONFIG_NODE, &ip_multicast_routing_cmd); + install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd); + install_element (CONFIG_NODE, &ip_ssmpingd_cmd); + install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd); +#if 0 + install_element (CONFIG_NODE, &interface_cmd); /* from if.h */ +#else + install_element (CONFIG_NODE, &pim_interface_cmd); +#endif + install_element (CONFIG_NODE, &no_interface_cmd); /* from if.h */ + + install_default (INTERFACE_NODE); + install_element (INTERFACE_NODE, &interface_ip_igmp_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element (INTERFACE_NODE, &interface_ip_pim_ssm_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); + + install_element (VIEW_NODE, &show_ip_igmp_interface_cmd); + install_element (VIEW_NODE, &show_ip_igmp_join_cmd); + install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd); + install_element (VIEW_NODE, &show_ip_igmp_groups_cmd); + install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element (VIEW_NODE, &show_ip_igmp_sources_cmd); + install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element (VIEW_NODE, &show_ip_igmp_querier_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element (VIEW_NODE, &show_ip_pim_dr_cmd); + install_element (VIEW_NODE, &show_ip_pim_hello_cmd); + install_element (VIEW_NODE, &show_ip_pim_interface_cmd); + install_element (VIEW_NODE, &show_ip_pim_join_cmd); + install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd); + install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd); + install_element (VIEW_NODE, &show_ip_pim_rpf_cmd); + install_element (VIEW_NODE, &show_ip_pim_secondary_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element (VIEW_NODE, &show_ip_multicast_cmd); + install_element (VIEW_NODE, &show_ip_mroute_cmd); + install_element (VIEW_NODE, &show_ip_mroute_count_cmd); + install_element (VIEW_NODE, &show_ip_rib_cmd); + install_element (VIEW_NODE, &show_ip_ssmpingd_cmd); + install_element (VIEW_NODE, &show_debugging_cmd); + + install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_mroute_cmd); + install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); + + install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_parameters_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_groups_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_sources_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_querier_cmd); + install_element (ENABLE_NODE, &show_ip_pim_address_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_internal_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_metric_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element (ENABLE_NODE, &show_ip_pim_dr_cmd); + install_element (ENABLE_NODE, &show_ip_pim_hello_cmd); + install_element (ENABLE_NODE, &show_ip_pim_interface_cmd); + install_element (ENABLE_NODE, &show_ip_pim_join_cmd); + install_element (ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element (ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element (ENABLE_NODE, &show_ip_pim_local_membership_cmd); + install_element (ENABLE_NODE, &show_ip_pim_neighbor_cmd); + install_element (ENABLE_NODE, &show_ip_pim_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_pim_secondary_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_multicast_cmd); + install_element (ENABLE_NODE, &show_ip_mroute_cmd); + install_element (ENABLE_NODE, &show_ip_mroute_count_cmd); + install_element (ENABLE_NODE, &show_ip_rib_cmd); + install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd); + install_element (ENABLE_NODE, &show_debugging_cmd); + + install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); + install_element (ENABLE_NODE, &test_pim_receive_assert_cmd); + install_element (ENABLE_NODE, &test_pim_receive_dump_cmd); + install_element (ENABLE_NODE, &test_pim_receive_hello_cmd); + install_element (ENABLE_NODE, &test_pim_receive_join_cmd); + install_element (ENABLE_NODE, &test_pim_receive_prune_cmd); + install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd); + + install_element (ENABLE_NODE, &debug_igmp_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_cmd); + install_element (ENABLE_NODE, &undebug_igmp_cmd); + install_element (ENABLE_NODE, &debug_igmp_events_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_events_cmd); + install_element (ENABLE_NODE, &undebug_igmp_events_cmd); + install_element (ENABLE_NODE, &debug_igmp_packets_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_packets_cmd); + install_element (ENABLE_NODE, &undebug_igmp_packets_cmd); + install_element (ENABLE_NODE, &debug_igmp_trace_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd); + install_element (ENABLE_NODE, &undebug_igmp_trace_cmd); + install_element (ENABLE_NODE, &debug_mroute_cmd); + install_element (ENABLE_NODE, &no_debug_mroute_cmd); + install_element (ENABLE_NODE, &debug_pim_cmd); + install_element (ENABLE_NODE, &no_debug_pim_cmd); + install_element (ENABLE_NODE, &undebug_pim_cmd); + install_element (ENABLE_NODE, &debug_pim_events_cmd); + install_element (ENABLE_NODE, &no_debug_pim_events_cmd); + install_element (ENABLE_NODE, &undebug_pim_events_cmd); + install_element (ENABLE_NODE, &debug_pim_packets_cmd); + install_element (ENABLE_NODE, &debug_pim_packets_filter_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packets_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packets_filter_cmd); + install_element (ENABLE_NODE, &undebug_pim_packets_cmd); + install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &undebug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &debug_pim_packetdump_recv_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packetdump_recv_cmd); + install_element (ENABLE_NODE, &undebug_pim_packetdump_recv_cmd); + install_element (ENABLE_NODE, &debug_pim_trace_cmd); + install_element (ENABLE_NODE, &no_debug_pim_trace_cmd); + install_element (ENABLE_NODE, &undebug_pim_trace_cmd); + install_element (ENABLE_NODE, &debug_ssmpingd_cmd); + install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd); + install_element (ENABLE_NODE, &undebug_ssmpingd_cmd); + install_element (ENABLE_NODE, &debug_pim_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element (ENABLE_NODE, &undebug_pim_zebra_cmd); + + install_element (CONFIG_NODE, &debug_igmp_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_cmd); + install_element (CONFIG_NODE, &undebug_igmp_cmd); + install_element (CONFIG_NODE, &debug_igmp_events_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_events_cmd); + install_element (CONFIG_NODE, &undebug_igmp_events_cmd); + install_element (CONFIG_NODE, &debug_igmp_packets_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_packets_cmd); + install_element (CONFIG_NODE, &undebug_igmp_packets_cmd); + install_element (CONFIG_NODE, &debug_igmp_trace_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd); + install_element (CONFIG_NODE, &undebug_igmp_trace_cmd); + install_element (CONFIG_NODE, &debug_mroute_cmd); + install_element (CONFIG_NODE, &no_debug_mroute_cmd); + install_element (CONFIG_NODE, &debug_pim_cmd); + install_element (CONFIG_NODE, &no_debug_pim_cmd); + install_element (CONFIG_NODE, &undebug_pim_cmd); + install_element (CONFIG_NODE, &debug_pim_events_cmd); + install_element (CONFIG_NODE, &no_debug_pim_events_cmd); + install_element (CONFIG_NODE, &undebug_pim_events_cmd); + install_element (CONFIG_NODE, &debug_pim_packets_cmd); + install_element (CONFIG_NODE, &debug_pim_packets_filter_cmd); + install_element (CONFIG_NODE, &no_debug_pim_packets_cmd); + install_element (CONFIG_NODE, &no_debug_pim_packets_filter_cmd); + install_element (CONFIG_NODE, &undebug_pim_packets_cmd); + install_element (CONFIG_NODE, &debug_pim_trace_cmd); + install_element (CONFIG_NODE, &no_debug_pim_trace_cmd); + install_element (CONFIG_NODE, &undebug_pim_trace_cmd); + install_element (CONFIG_NODE, &debug_ssmpingd_cmd); + install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd); + install_element (CONFIG_NODE, &undebug_ssmpingd_cmd); + install_element (CONFIG_NODE, &debug_pim_zebra_cmd); + install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element (CONFIG_NODE, &undebug_pim_zebra_cmd); +} diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h new file mode 100644 index 00000000..c5037400 --- /dev/null +++ b/pimd/pim_cmd.h @@ -0,0 +1,66 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_CMD_H +#define PIM_CMD_H + +#define PIM_STR "PIM information\n" +#define IGMP_STR "IGMP information\n" +#define IGMP_GROUP_STR "IGMP groups information\n" +#define IGMP_SOURCE_STR "IGMP sources information\n" +#define CONF_SSMPINGD_STR "Enable ssmpingd operation\n" +#define SHOW_SSMPINGD_STR "ssmpingd operation\n" +#define IFACE_PIM_STR "Enable PIM SSM operation\n" +#define IFACE_IGMP_STR "Enable IGMP operation\n" +#define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" +#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n" +#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n" +#define DEBUG_IGMP_STR "IGMP protocol activity\n" +#define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" +#define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" +#define DEBUG_IGMP_TRACE_STR "IGMP internal daemon activity\n" +#define DEBUG_MROUTE_STR "PIM interaction with kernel MFC cache\n" +#define DEBUG_PIM_STR "PIM protocol activity\n" +#define DEBUG_PIM_EVENTS_STR "PIM protocol events\n" +#define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" +#define DEBUG_PIM_HELLO_PACKETS_STR "PIM Hello protocol packets\n" +#define DEBUG_PIM_J_P_PACKETS_STR "PIM Join/Prune protocol packets\n" +#define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n" +#define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n" +#define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" +#define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" +#define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define DEBUG_SSMPINGD_STR "ssmpingd activity\n" +#define CLEAR_IP_IGMP_STR "IGMP clear commands\n" +#define CLEAR_IP_PIM_STR "PIM clear commands\n" +#define MROUTE_STR "IP multicast routing table\n" +#define RIB_STR "IP unicast routing table\n" + +#define PIM_CMD_NO "no" +#define PIM_CMD_IP_MULTICAST_ROUTING "ip multicast-routing" +#define PIM_CMD_IP_IGMP_QUERY_INTERVAL "ip igmp query-interval" +#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME "ip igmp query-max-response-time" +#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "ip igmp query-max-response-time-dsec" + +void pim_cmd_init(void); + +#endif /* PIM_CMD_H */ diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c new file mode 100644 index 00000000..12857831 --- /dev/null +++ b/pimd/pim_hello.c @@ -0,0 +1,529 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_util.h" +#include "pim_hello.h" +#include "pim_iface.h" +#include "pim_neighbor.h" +#include "pim_upstream.h" + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static void tlv_trace_bool(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, int value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint16(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint16_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint32(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint32_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint32_hex(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint32_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x", + label, + src_str, ifname, + tlv_name, value); + } +} + +#if 0 +static void tlv_trace(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s", + label, + src_str, ifname, + tlv_name); + } +} +#endif + +static void tlv_trace_list(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, struct list *addr_list) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p", + label, + src_str, ifname, + tlv_name, + addr_list ? ((int) listcount(addr_list)) : -1, + (void *) addr_list); + } +} + +#define FREE_ADDR_LIST \ + if (hello_option_addr_list) { \ + list_delete(hello_option_addr_list); \ + } + +#define FREE_ADDR_LIST_THEN_RETURN(code) \ +{ \ + FREE_ADDR_LIST \ + return (code); \ +} + +int pim_hello_recv(struct interface *ifp, + struct in_addr src_addr, + uint8_t *tlv_buf, int tlv_buf_size) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + uint8_t *tlv_curr; + uint8_t *tlv_pastend; + pim_hello_options hello_options = 0; /* bit array recording options found */ + uint16_t hello_option_holdtime = 0; + uint16_t hello_option_propagation_delay = 0; + uint16_t hello_option_override_interval = 0; + uint32_t hello_option_dr_priority = 0; + uint32_t hello_option_generation_id = 0; + struct list *hello_option_addr_list = 0; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + pim_ifp = ifp->info; + zassert(pim_ifp); + + ++pim_ifp->pim_ifstat_hello_recv; + + /* + Parse PIM hello TLVs + */ + zassert(tlv_buf_size >= 0); + tlv_curr = tlv_buf; + tlv_pastend = tlv_buf + tlv_buf_size; + + while (tlv_curr < tlv_pastend) { + uint16_t option_type; + uint16_t option_len; + int remain = tlv_pastend - tlv_curr; + + if (remain < PIM_TLV_MIN_SIZE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s", + __PRETTY_FUNCTION__, + remain, PIM_TLV_MIN_SIZE, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-1); + } + + option_type = PIM_TLV_GET_TYPE(tlv_curr); + tlv_curr += PIM_TLV_TYPE_SIZE; + option_len = PIM_TLV_GET_LENGTH(tlv_curr); + tlv_curr += PIM_TLV_LENGTH_SIZE; + + if ((tlv_curr + option_len) > tlv_pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, tlv_pastend - tlv_curr, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-2); + } + + if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, + option_type, option_len, + src_str, ifp->name); + } + + switch (option_type) { + case PIM_MSG_OPTION_TYPE_HOLDTIME: + if (pim_tlv_parse_holdtime(ifp->name, src_addr, + &hello_options, + &hello_option_holdtime, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-3); + } + break; + case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY: + if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr, + &hello_options, + &hello_option_propagation_delay, + &hello_option_override_interval, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-4); + } + break; + case PIM_MSG_OPTION_TYPE_DR_PRIORITY: + if (pim_tlv_parse_dr_priority(ifp->name, src_addr, + &hello_options, + &hello_option_dr_priority, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-5); + } + break; + case PIM_MSG_OPTION_TYPE_GENERATION_ID: + if (pim_tlv_parse_generation_id(ifp->name, src_addr, + &hello_options, + &hello_option_generation_id, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-6); + } + break; + case PIM_MSG_OPTION_TYPE_ADDRESS_LIST: + if (pim_tlv_parse_addr_list(ifp->name, src_addr, + &hello_options, + &hello_option_addr_list, + option_len, + tlv_curr)) { + return -7; + } + break; + case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH: + if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, + src_str, ifp->name); + } + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, + src_str, ifp->name); + } + } + + tlv_curr += option_len; + } + + /* + Check received PIM hello options + */ + + if (PIM_DEBUG_PIM_TRACE) { + tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME), + hello_option_holdtime); + tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + hello_option_propagation_delay); + tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + hello_option_override_interval); + tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)); + tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY), + hello_option_dr_priority); + tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID), + hello_option_generation_id); + tlv_trace_list(__PRETTY_FUNCTION__, "address_list", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST), + hello_option_addr_list); + } + + if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello missing holdtime from %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + /* + New neighbor? + */ + + neigh = pim_neighbor_find(ifp, src_addr); + if (!neigh) { + /* Add as new neighbor */ + + neigh = pim_neighbor_add(ifp, src_addr, + hello_options, + hello_option_holdtime, + hello_option_propagation_delay, + hello_option_override_interval, + hello_option_dr_priority, + hello_option_generation_id, + hello_option_addr_list); + if (!neigh) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: failure creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-8); + } + + /* actual addr list has been saved under neighbor */ + return 0; + } + + /* + Received generation ID ? + */ + + if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) { + /* GenID mismatch ? */ + if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) || + (hello_option_generation_id != neigh->generation_id)) { + + /* GenID changed */ + + pim_upstream_rpf_genid_changed(neigh->source_addr); + + /* GenID mismatch, then replace neighbor */ + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s", + __PRETTY_FUNCTION__, + hello_option_generation_id, + neigh->generation_id, + src_str, ifp->name); + } + + pim_upstream_rpf_genid_changed(neigh->source_addr); + + pim_neighbor_delete(ifp, neigh, "GenID mismatch"); + neigh = pim_neighbor_add(ifp, src_addr, + hello_options, + hello_option_holdtime, + hello_option_propagation_delay, + hello_option_override_interval, + hello_option_dr_priority, + hello_option_generation_id, + hello_option_addr_list); + if (!neigh) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: failure re-creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-9); + } + /* actual addr list is saved under neighbor */ + return 0; + + } /* GenId mismatch: replace neighbor */ + + } /* GenId received */ + + /* + Update existing neighbor + */ + + pim_neighbor_update(neigh, + hello_options, + hello_option_holdtime, + hello_option_dr_priority, + hello_option_addr_list); + /* actual addr list is saved under neighbor */ + return 0; +} + +int pim_hello_build_tlv(const char *ifname, + uint8_t *tlv_buf, int tlv_buf_size, + uint16_t holdtime, + uint32_t dr_priority, + uint32_t generation_id, + uint16_t propagation_delay, + uint16_t override_interval, + int can_disable_join_suppression, + struct list *ifconnected) +{ + uint8_t *curr = tlv_buf; + uint8_t *pastend = tlv_buf + tlv_buf_size; + uint8_t *tmp; + + /* + * Append options + */ + + /* Holdtime */ + curr = pim_tlv_append_uint16(curr, + pastend, + PIM_MSG_OPTION_TYPE_HOLDTIME, + holdtime); + if (!curr) { + zlog_warn("%s: could not set PIM hello Holdtime option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -1; + } + + /* LAN Prune Delay */ + tmp = pim_tlv_append_2uint16(curr, + pastend, + PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY, + propagation_delay, + override_interval); + if (!tmp) { + zlog_warn("%s: could not set PIM LAN Prune Delay option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -1; + } + if (can_disable_join_suppression) { + *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */ + } + curr = tmp; + + /* DR Priority */ + curr = pim_tlv_append_uint32(curr, + pastend, + PIM_MSG_OPTION_TYPE_DR_PRIORITY, + dr_priority); + if (!curr) { + zlog_warn("%s: could not set PIM hello DR Priority option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -2; + } + + /* Generation ID */ + curr = pim_tlv_append_uint32(curr, + pastend, + PIM_MSG_OPTION_TYPE_GENERATION_ID, + generation_id); + if (!curr) { + zlog_warn("%s: could not set PIM hello Generation ID option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -3; + } + + /* Secondary Address List */ + if (ifconnected) { + curr = pim_tlv_append_addrlist_ucast(curr, + pastend, + ifconnected); + if (!curr) { + zlog_warn("%s: could not set PIM hello Secondary Address List option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -4; + } + } + + return curr - tlv_buf; +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on an + interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. +*/ +void pim_hello_require(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + + zassert(pim_ifp); + + if (pim_ifp->pim_ifstat_hello_sent) + return; + + pim_hello_restart_now(ifp); /* Send hello and restart timer */ +} diff --git a/pimd/pim_hello.h b/pimd/pim_hello.h new file mode 100644 index 00000000..b5e272d5 --- /dev/null +++ b/pimd/pim_hello.h @@ -0,0 +1,46 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_HELLO_H +#define PIM_HELLO_H + +#include + +#include "if.h" + +int pim_hello_recv(struct interface *ifp, + struct in_addr src_addr, + uint8_t *tlv_buf, int tlv_buf_size); + +int pim_hello_build_tlv(const char *ifname, + uint8_t *tlv_buf, int tlv_buf_size, + uint16_t holdtime, + uint32_t dr_priority, + uint32_t generation_id, + uint16_t propagation_delay, + uint16_t override_interval, + int can_disable_join_suppression, + struct list *ifconnected); + +void pim_hello_require(struct interface *ifp); + +#endif /* PIM_HELLO_H */ diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c new file mode 100644 index 00000000..ecf9ef6b --- /dev/null +++ b/pimd/pim_iface.c @@ -0,0 +1,1229 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "if.h" +#include "log.h" +#include "vty.h" +#include "memory.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_igmp.h" +#include "pim_mroute.h" +#include "pim_oil.h" +#include "pim_str.h" +#include "pim_pim.h" +#include "pim_neighbor.h" +#include "pim_ifchannel.h" +#include "pim_rand.h" +#include "pim_sock.h" +#include "pim_time.h" +#include "pim_ssmpingd.h" + +static void pim_if_igmp_join_del_all(struct interface *ifp); + +void pim_if_init() +{ + if_init(); +} + +static void *if_list_clean(struct pim_interface *pim_ifp) +{ + if (pim_ifp->igmp_join_list) { + list_delete(pim_ifp->igmp_join_list); + } + + if (pim_ifp->igmp_socket_list) { + list_delete(pim_ifp->igmp_socket_list); + } + + if (pim_ifp->pim_neighbor_list) { + list_delete(pim_ifp->pim_neighbor_list); + } + + if (pim_ifp->pim_ifchannel_list) { + list_delete(pim_ifp->pim_ifchannel_list); + } + + XFREE(MTYPE_PIM_INTERFACE, pim_ifp); + + return 0; +} + +struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(!ifp->info); + + pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); + if (!pim_ifp) { + zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp)); + return 0; + } + + pim_ifp->options = 0; + pim_ifp->mroute_vif_index = -1; + + pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; + pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + + /* + RFC 3376: 8.3. Query Response Interval + The number of seconds represented by the [Query Response Interval] + must be less than the [Query Interval]. + */ + zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval); + + if (pim) + PIM_IF_DO_PIM(pim_ifp->options); + if (igmp) + PIM_IF_DO_IGMP(pim_ifp->options); + +#if 0 + /* FIXME: Should join? */ + PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); +#endif + + pim_ifp->igmp_join_list = 0; + pim_ifp->igmp_socket_list = 0; + pim_ifp->pim_neighbor_list = 0; + pim_ifp->pim_ifchannel_list = 0; + + /* list of struct igmp_sock */ + pim_ifp->igmp_socket_list = list_new(); + if (!pim_ifp->igmp_socket_list) { + zlog_err("%s %s: failure: igmp_socket_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free; + + /* list of struct pim_neighbor */ + pim_ifp->pim_neighbor_list = list_new(); + if (!pim_ifp->pim_neighbor_list) { + zlog_err("%s %s: failure: pim_neighbor_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free; + + /* list of struct pim_ifchannel */ + pim_ifp->pim_ifchannel_list = list_new(); + if (!pim_ifp->pim_ifchannel_list) { + zlog_err("%s %s: failure: pim_ifchannel_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free; + + ifp->info = pim_ifp; + + pim_sock_reset(ifp); + + zassert(PIM_IF_TEST_PIM(pim_ifp->options) || PIM_IF_TEST_IGMP(pim_ifp->options)); + + if (PIM_MROUTE_IS_ENABLED) { + pim_if_add_vif(ifp); + } + + return pim_ifp; +} + +void pim_if_delete(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (pim_ifp->igmp_join_list) { + pim_if_igmp_join_del_all(ifp); + } + zassert(!pim_ifp->igmp_join_list); + + zassert(pim_ifp->igmp_socket_list); + zassert(!listcount(pim_ifp->igmp_socket_list)); + + zassert(pim_ifp->pim_neighbor_list); + zassert(!listcount(pim_ifp->pim_neighbor_list)); + + zassert(pim_ifp->pim_ifchannel_list); + zassert(!listcount(pim_ifp->pim_ifchannel_list)); + + if (PIM_MROUTE_IS_ENABLED) { + pim_if_del_vif(ifp); + } + + list_delete(pim_ifp->igmp_socket_list); + list_delete(pim_ifp->pim_neighbor_list); + list_delete(pim_ifp->pim_ifchannel_list); + + XFREE(MTYPE_PIM_INTERFACE, pim_ifp); + + ifp->info = 0; +} + +void pim_if_update_could_assert(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_could_assert(ch); + } +} + +static void pim_if_update_my_assert_metric(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_my_assert_metric(ch); + } +} + +static void pim_addr_change(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_if_dr_election(ifp); /* router's own DR Priority (addr) changes -- Done TODO T30 */ + pim_if_update_join_desired(pim_ifp); /* depends on DR */ + pim_if_update_could_assert(ifp); /* depends on DR */ + pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ + pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */ + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + 1) Before an interface goes down or changes primary IP address, a + Hello message with a zero HoldTime should be sent immediately + (with the old IP address if the IP address changed). + -- FIXME See CAVEAT C13 + + 2) After an interface has changed its IP address, it MUST send a + Hello message with its new IP address. + -- DONE below + + 3) If an interface changes one of its secondary IP addresses, a + Hello message with an updated Address_List option and a non-zero + HoldTime should be sent immediately. + -- FIXME See TODO T31 + */ + pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */ + if (pim_ifp->pim_sock_fd < 0) + return; + pim_hello_restart_now(ifp); /* send hello and restart timer */ +} + +static void on_primary_address_change(struct interface *ifp, + const char *caller, + struct in_addr old_addr, + struct in_addr new_addr) +{ + struct pim_interface *pim_ifp; + + { + char old_str[100]; + char new_str[100]; + pim_inet4_dump("", old_addr, old_str, sizeof(old_str)); + pim_inet4_dump("", new_addr, new_str, sizeof(new_str)); + zlog_info("%s: %s: primary address changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, caller, + old_str, new_str, ifp->name); + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + return; + } + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + pim_addr_change(ifp); +} + +static int detect_primary_address_change(struct interface *ifp, + int force_prim_as_any, + const char *caller) +{ + struct pim_interface *pim_ifp; + struct in_addr new_prim_addr; + int changed; + + pim_ifp = ifp->info; + if (!pim_ifp) + return 0; + + if (force_prim_as_any) + new_prim_addr = qpim_inaddr_any; + else + new_prim_addr = pim_find_primary_addr(ifp); + + changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; + + if (PIM_DEBUG_ZEBRA) { + char new_prim_str[100]; + char old_prim_str[100]; + pim_inet4_dump("", new_prim_addr, new_prim_str, sizeof(new_prim_str)); + pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); + zlog_debug("%s: old=%s new=%s on interface %s: %s", + __PRETTY_FUNCTION__, + old_prim_str, new_prim_str, ifp->name, + changed ? "changed" : "unchanged"); + } + + if (changed) { + struct in_addr old_addr = pim_ifp->primary_address; + pim_ifp->primary_address = new_prim_addr; + + on_primary_address_change(ifp, caller, old_addr, new_prim_addr); + } + + return changed; +} + +static void detect_secondary_address_change(struct interface *ifp, + const char *caller) +{ + struct pim_interface *pim_ifp; + int changed; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + changed = 1; /* true */ + zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change", + __PRETTY_FUNCTION__, ifp->name); + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: on interface %s: %s", + __PRETTY_FUNCTION__, + ifp->name, changed ? "changed" : "unchanged"); + } + + if (!changed) { + return; + } + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + pim_addr_change(ifp); +} + +static void detect_address_change(struct interface *ifp, + int force_prim_as_any, + const char *caller) +{ + int prim_changed; + + prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller); + if (prim_changed) { + /* no need to detect secondary change because + the reaction would be the same */ + return; + } + + detect_secondary_address_change(ifp, caller); +} + +void pim_if_addr_add(struct connected *ifc) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + struct in_addr ifaddr; + + zassert(ifc); + + ifp = ifc->ifp; + zassert(ifp); + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + if (!if_is_operative(ifp)) + return; + + /* if (PIM_DEBUG_ZEBRA) */ { + char buf[BUFSIZ]; + prefix2str(ifc->address, buf, BUFSIZ); + zlog_debug("%s: %s ifindex=%d connected IP address %s %s", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, buf, + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); + } + + ifaddr = ifc->address->u.prefix4; + + detect_address_change(ifp, 0, __PRETTY_FUNCTION__); + + if (PIM_IF_TEST_IGMP(pim_ifp->options)) { + struct igmp_sock *igmp; + + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, + ifaddr); + if (!igmp) { + /* if addr new, add IGMP socket */ + pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp); + } + } /* igmp */ + + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + + /* Interface has a valid primary address ? */ + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + + /* Interface has a valid socket ? */ + if (pim_ifp->pim_sock_fd < 0) { + if (pim_sock_add(ifp)) { + zlog_warn("Failure creating PIM socket for interface %s", + ifp->name); + } + } + + } + } /* pim */ + + if (PIM_MROUTE_IS_ENABLED) { + /* + PIM or IGMP is enabled on interface, and there is at least one + address assigned, then try to create a vif_index. + */ + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); + } + } +} + +static void pim_if_addr_del_igmp(struct connected *ifc) +{ + struct pim_interface *pim_ifp = ifc->ifp->info; + struct igmp_sock *igmp; + struct in_addr ifaddr; + + if (ifc->address->family != AF_INET) { + /* non-IPv4 address */ + return; + } + + if (!pim_ifp) { + /* IGMP not enabled on interface */ + return; + } + + ifaddr = ifc->address->u.prefix4; + + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, + ifaddr); + if (igmp) { + /* if addr found, del IGMP socket */ + igmp_sock_delete(igmp); + } +} + +static void pim_if_addr_del_pim(struct connected *ifc) +{ + struct pim_interface *pim_ifp = ifc->ifp->info; + + if (ifc->address->family != AF_INET) { + /* non-IPv4 address */ + return; + } + + if (!pim_ifp) { + /* PIM not enabled on interface */ + return; + } + + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + /* Interface keeps a valid primary address */ + return; + } + + if (pim_ifp->pim_sock_fd < 0) { + /* Interface does not hold a valid socket any longer */ + return; + } + + /* + pim_sock_delete() closes the socket, stops read and timer threads, + and kills all neighbors. + */ + pim_sock_delete(ifc->ifp, "last address has been removed from interface"); +} + +void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) +{ + struct interface *ifp; + + zassert(ifc); + ifp = ifc->ifp; + zassert(ifp); + + /* if (PIM_DEBUG_ZEBRA) */ { + char buf[BUFSIZ]; + prefix2str(ifc->address, buf, BUFSIZ); + zlog_debug("%s: %s ifindex=%d disconnected IP address %s %s", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, buf, + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); + } + + detect_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); + + pim_if_addr_del_igmp(ifc); + pim_if_addr_del_pim(ifc); +} + +void pim_if_addr_add_all(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_add(ifc); + } +} + +void pim_if_addr_del_all(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); + } +} + +void pim_if_addr_del_all_igmp(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del_igmp(ifc); + } +} + +void pim_if_addr_del_all_pim(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del_pim(ifc); + } +} + +static struct in_addr find_first_nonsec_addr(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct in_addr addr; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if (PIM_INADDR_IS_ANY(p->u.prefix4)) { + zlog_warn("%s: null IPv4 address connected to interface %s", + __PRETTY_FUNCTION__, ifp->name); + continue; + } + + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) + continue; + + return p->u.prefix4; + } + + addr.s_addr = PIM_NET_INADDR_ANY; + + return addr; +} + +struct in_addr pim_find_primary_addr(struct interface *ifp) +{ + return find_first_nonsec_addr(ifp); +} + +/* + pim_if_add_vif() uses ifindex as vif_index + + see also pim_if_find_vifindex_by_ifindex() + */ +int pim_if_add_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct in_addr ifaddr; + + zassert(pim_ifp); + + if (pim_ifp->mroute_vif_index > 0) { + zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d", + __PRETTY_FUNCTION__, + pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); + return -1; + } + + if (ifp->ifindex < 1) { + zlog_warn("%s: ifindex=%d < 1 on interface %s", + __PRETTY_FUNCTION__, + ifp->ifindex, ifp->name); + return -2; + } + + if (ifp->ifindex >= MAXVIFS) { + zlog_warn("%s: ifindex=%d >= MAXVIFS=%d on interface %s", + __PRETTY_FUNCTION__, + ifp->ifindex, MAXVIFS, ifp->name); + return -3; + } + + ifaddr = pim_ifp->primary_address; + if (PIM_INADDR_IS_ANY(ifaddr)) { + zlog_warn("%s: could not get address for interface %s ifindex=%d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex); + return -4; + } + + if (pim_mroute_add_vif(ifp->ifindex, ifaddr)) { + /* pim_mroute_add_vif reported error */ + return -5; + } + + pim_ifp->mroute_vif_index = ifp->ifindex; + + /* + Update highest vif_index + */ + if (pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) { + qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index; + } + + return 0; +} + +static int iflist_find_highest_vif_index() +{ + struct listnode *ifnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + int highest_vif_index = -1; + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + if (pim_ifp->mroute_vif_index > highest_vif_index) { + highest_vif_index = pim_ifp->mroute_vif_index; + } + } + + return highest_vif_index; +} + +int pim_if_del_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + int old_vif_index; + + if (pim_ifp->mroute_vif_index < 1) { + zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d", + __PRETTY_FUNCTION__, + pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); + return -1; + } + + if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) { + /* pim_mroute_del_vif reported error */ + return -2; + } + + /* + Update highest vif_index + */ + + /* save old vif_index in order to compare with highest below */ + old_vif_index = pim_ifp->mroute_vif_index; + + pim_ifp->mroute_vif_index = -1; + + if (old_vif_index == qpim_mroute_oif_highest_vif_index) { + qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index(); + } + + return 0; +} + +void pim_if_add_vif_all() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (!ifp->info) + continue; + + pim_if_add_vif(ifp); + } +} + +void pim_if_del_vif_all() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (!ifp->info) + continue; + + pim_if_del_vif(ifp); + } +} + +struct interface *pim_if_find_by_vif_index(int vif_index) +{ + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + if (ifp->info) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + if (vif_index == pim_ifp->mroute_vif_index) + return ifp; + } + } + + return 0; +} + +/* + pim_if_add_vif() uses ifindex as vif_index + */ +int pim_if_find_vifindex_by_ifindex(int ifindex) +{ + return ifindex; +} + +int pim_if_lan_delay_enabled(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0); + + return pim_ifp->pim_number_of_nonlandelay_neighbors == 0; +} + +uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp) +{ + if (pim_if_lan_delay_enabled(ifp)) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + return pim_ifp->pim_neighbors_highest_propagation_delay_msec; + } + else { + return PIM_DEFAULT_PROPAGATION_DELAY_MSEC; + } +} + +uint16_t pim_if_effective_override_interval_msec(struct interface *ifp) +{ + if (pim_if_lan_delay_enabled(ifp)) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + return pim_ifp->pim_neighbors_highest_override_interval_msec; + } + else { + return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; + } +} + +int pim_if_t_override_msec(struct interface *ifp) +{ + int effective_override_interval_msec; + int t_override_msec; + + effective_override_interval_msec = + pim_if_effective_override_interval_msec(ifp); + + t_override_msec = pim_rand_next(0, effective_override_interval_msec); + + return t_override_msec; +} + +uint16_t pim_if_jp_override_interval_msec(struct interface *ifp) +{ + return pim_if_effective_propagation_delay_msec(ifp) + + pim_if_effective_override_interval_msec(ifp); +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + The function NBR( I, A ) uses information gathered through PIM Hello + messages to map the IP address A of a directly connected PIM + neighbor router on interface I to the primary IP address of the same + router (Section 4.3.4). The primary IP address of a neighbor is the + address that it uses as the source of its PIM Hello messages. +*/ +struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, + struct in_addr addr) +{ + struct listnode *neighnode; + struct pim_neighbor *neigh; + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + + /* primary address ? */ + if (neigh->source_addr.s_addr == addr.s_addr) + return neigh; + + /* secondary address ? */ + if (pim_neighbor_find_secondary(neigh, addr)) + return neigh; + } + + if (PIM_DEBUG_PIM_TRACE) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: neighbor not found for address %s on interface %s", + __PRETTY_FUNCTION__, + addr_str, ifp->name); + } + + return 0; +} + +long pim_if_t_suppressed_msec(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + long t_suppressed_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + /* join suppression disabled ? */ + if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)) + return 0; + + /* t_suppressed = t_periodic * rand(1.1, 1.4) */ + + t_suppressed_msec = qpim_t_periodic * pim_rand_next(1100, 1400); + + return t_suppressed_msec; +} + +static void igmp_join_free(struct igmp_join *ij) +{ + XFREE(MTYPE_PIM_IGMP_JOIN, ij); +} + +static struct igmp_join *igmp_join_find(struct list *join_list, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct listnode *node; + struct igmp_join *ij; + + zassert(join_list); + + for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { + if ((group_addr.s_addr == ij->group_addr.s_addr) && + (source_addr.s_addr == ij->source_addr.s_addr)) + return ij; + } + + return 0; +} + +static int igmp_join_sock(const char *ifname, + int ifindex, + struct in_addr group_addr, + struct in_addr source_addr) +{ + int join_fd; + + join_fd = pim_socket_raw(IPPROTO_IGMP); + if (join_fd < 0) { + return -1; + } + + if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) { + close(join_fd); + return -2; + } + + return join_fd; +} + +static struct igmp_join *igmp_join_new(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + int join_fd; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr); + if (join_fd < 0) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return 0; + } + + ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + sizeof(*ij), group_str, source_str, ifp->name); + close(join_fd); + return 0; + } + + ij->sock_fd = join_fd; + ij->group_addr = group_addr; + ij->source_addr = source_addr; + ij->sock_creation = pim_time_monotonic_sec(); + + listnode_add(pim_ifp->igmp_join_list, ij); + + return ij; +} + +int pim_if_igmp_join_add(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (!pim_ifp->igmp_join_list) { + pim_ifp->igmp_join_list = list_new(); + if (!pim_ifp->igmp_join_list) { + zlog_err("%s %s: failure: igmp_join_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return -2; + } + pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free; + } + + ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); + if (ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -3; + } + + ij = igmp_join_new(ifp, group_addr, source_addr); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -4; + } + + { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ifp->name); + } + + return 0; +} + + + +int pim_if_igmp_join_del(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (!pim_ifp->igmp_join_list) { + zlog_warn("%s: no IGMP join on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -2; + } + + ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: could not find IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -3; + } + + if (close(ij->sock_fd)) { + int e = errno; + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", + __PRETTY_FUNCTION__, + ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e)); + /* warning only */ + } + listnode_delete(pim_ifp->igmp_join_list, ij); + igmp_join_free(ij); + if (listcount(pim_ifp->igmp_join_list) < 1) { + list_delete(pim_ifp->igmp_join_list); + pim_ifp->igmp_join_list = 0; + } + + return 0; +} + +static void pim_if_igmp_join_del_all(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *nextnode; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return; + } + + if (!pim_ifp->igmp_join_list) + return; + + for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij)) + pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr); +} + +/* + RFC 4601 + + Transitions from "I am Assert Loser" State + + Current Winner's GenID Changes or NLT Expires + + The Neighbor Liveness Timer associated with the current winner + expires or we receive a Hello message from the current winner + reporting a different GenID from the one it previously reported. + This indicates that the current winner's interface or router has + gone down (and may have come back up), and so we must assume it no + longer knows it was the winner. + */ +void pim_if_assert_on_neighbor_down(struct interface *ifp, + struct in_addr neigh_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + /* Is (S,G,I) assert loser ? */ + if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) + continue; + /* Dead neighbor was winner ? */ + if (ch->ifassert_winner.s_addr != neigh_addr.s_addr) + continue; + + assert_action_a5(ch); + } +} + +void pim_if_update_join_desired(struct pim_interface *pim_ifp) +{ + struct listnode *ch_node; + struct pim_ifchannel *ch; + + /* clear off flag from interface's upstreams */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags); + } + + /* scan per-interface (S,G,I) state on this I interface */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + struct pim_upstream *up = ch->upstream; + + if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags)) + continue; + + /* update join_desired for the global (S,G) state */ + pim_upstream_update_join_desired(up); + PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags); + } +} + +void pim_if_update_assert_tracking_desired(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_assert_tracking_desired(ch); + } +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h new file mode 100644 index 00000000..4b06b9ff --- /dev/null +++ b/pimd/pim_iface.h @@ -0,0 +1,161 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IFACE_H +#define PIM_IFACE_H + +#include + +#include "if.h" +#include "vty.h" + +#include "pim_igmp.h" +#include "pim_upstream.h" + +#define PIM_IF_MASK_PIM (1 << 0) +#define PIM_IF_MASK_IGMP (1 << 1) +#define PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS (1 << 2) +#define PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION (1 << 3) + +#define PIM_IF_IS_DELETED(ifp) ((ifp)->ifindex == IFINDEX_INTERNAL) + +#define PIM_IF_TEST_PIM(options) (PIM_IF_MASK_PIM & (options)) +#define PIM_IF_TEST_IGMP(options) (PIM_IF_MASK_IGMP & (options)) +#define PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(options) (PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS & (options)) +#define PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) (PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION & (options)) + +#define PIM_IF_DO_PIM(options) ((options) |= PIM_IF_MASK_PIM) +#define PIM_IF_DO_IGMP(options) ((options) |= PIM_IF_MASK_IGMP) +#define PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(options) ((options) |= PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS) +#define PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) |= PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION) + +#define PIM_IF_DONT_PIM(options) ((options) &= ~PIM_IF_MASK_PIM) +#define PIM_IF_DONT_IGMP(options) ((options) &= ~PIM_IF_MASK_IGMP) +#define PIM_IF_DONT_IGMP_LISTEN_ALLROUTERS(options) ((options) &= ~PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS) +#define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION) + +struct pim_interface { + uint32_t options; /* bit vector */ + int mroute_vif_index; + struct in_addr primary_address; /* remember addr to detect change */ + + int igmp_default_robustness_variable; /* IGMPv3 QRV */ + int igmp_default_query_interval; /* IGMPv3 secs between general queries */ + int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ + int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for specific queries */ + struct list *igmp_socket_list; /* list of struct igmp_sock */ + struct list *igmp_join_list; /* list of struct igmp_join */ + + int pim_sock_fd; /* PIM socket file descriptor */ + struct thread *t_pim_sock_read; /* thread for reading PIM socket */ + int64_t pim_sock_creation; /* timestamp of PIM socket creation */ + + struct thread *t_pim_hello_timer; + int pim_hello_period; + int pim_default_holdtime; + int pim_triggered_hello_delay; + uint32_t pim_generation_id; + uint16_t pim_propagation_delay_msec; /* config */ + uint16_t pim_override_interval_msec; /* config */ + struct list *pim_neighbor_list; /* list of struct pim_neighbor */ + struct list *pim_ifchannel_list; /* list of struct pim_ifchannel */ + + /* neighbors without lan_delay */ + int pim_number_of_nonlandelay_neighbors; + uint16_t pim_neighbors_highest_propagation_delay_msec; + uint16_t pim_neighbors_highest_override_interval_msec; + + /* DR Election */ + int64_t pim_dr_election_last; /* timestamp */ + int pim_dr_election_count; + int pim_dr_election_changes; + struct in_addr pim_dr_addr; + uint32_t pim_dr_priority; /* config */ + int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */ + + int64_t pim_ifstat_start; /* start timestamp for stats */ + uint32_t pim_ifstat_hello_sent; + uint32_t pim_ifstat_hello_sendfail; + uint32_t pim_ifstat_hello_recv; + uint32_t pim_ifstat_hello_recvfail; +}; + +/* + if default_holdtime is set (>= 0), use it; + otherwise default_holdtime is 3.5 * hello_period + */ +#define PIM_IF_DEFAULT_HOLDTIME(pim_ifp) \ + (((pim_ifp)->pim_default_holdtime < 0) ? \ + ((pim_ifp)->pim_hello_period * 7 / 2) : \ + ((pim_ifp)->pim_default_holdtime)) + +void pim_if_init(void); + +struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim); +void pim_if_delete(struct interface *ifp); +void pim_if_addr_add(struct connected *ifc); +void pim_if_addr_del(struct connected *ifc, int force_prim_as_any); +void pim_if_addr_add_all(struct interface *ifp); +void pim_if_addr_del_all(struct interface *ifp); +void pim_if_addr_del_all_igmp(struct interface *ifp); +void pim_if_addr_del_all_pim(struct interface *ifp); + +int pim_if_add_vif(struct interface *ifp); +int pim_if_del_vif(struct interface *ifp); +void pim_if_add_vif_all(void); +void pim_if_del_vif_all(void); + +struct interface *pim_if_find_by_vif_index(int vif_index); +int pim_if_find_vifindex_by_ifindex(int ifindex); + +int pim_if_lan_delay_enabled(struct interface *ifp); +uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp); +uint16_t pim_if_effective_override_interval_msec(struct interface *ifp); +uint16_t pim_if_jp_override_interval_msec(struct interface *ifp); +struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, + struct in_addr addr); + +long pim_if_t_suppressed_msec(struct interface *ifp); +int pim_if_t_override_msec(struct interface *ifp); + +struct in_addr pim_find_primary_addr(struct interface *ifp); + +int pim_if_igmp_join_add(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr); +int pim_if_igmp_join_del(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr); + +void pim_if_update_could_assert(struct interface *ifp); + +void pim_if_assert_on_neighbor_down(struct interface *ifp, + struct in_addr neigh_addr); + +void pim_if_rpf_interface_changed(struct interface *old_rpf_ifp, + struct pim_upstream *up); + +void pim_if_update_join_desired(struct pim_interface *pim_ifp); + +void pim_if_update_assert_tracking_desired(struct interface *ifp); + +#endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c new file mode 100644 index 00000000..e253a0ea --- /dev/null +++ b/pimd/pim_ifchannel.c @@ -0,0 +1,893 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "linklist.h" +#include "thread.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_ifchannel.h" +#include "pim_zebra.h" +#include "pim_time.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_join.h" +#include "pim_rpf.h" +#include "pim_macro.h" + +void pim_ifchannel_free(struct pim_ifchannel *ch) +{ + zassert(!ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + zassert(!ch->t_ifassert_timer); + + XFREE(MTYPE_PIM_IFCHANNEL, ch); +} + +void pim_ifchannel_delete(struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ch->interface->info; + zassert(pim_ifp); + + if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { + pim_upstream_update_join_desired(ch->upstream); + } + + pim_upstream_del(ch->upstream); + + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifassert_timer); + + /* + notice that listnode_delete() can't be moved + into pim_ifchannel_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(pim_ifp->pim_ifchannel_list, ch); + + pim_ifchannel_free(ch); +} + +#define IFCHANNEL_NOINFO(ch) \ + ( \ + ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \ + && \ + ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \ + && \ + ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \ + ) + +static void delete_on_noinfo(struct pim_ifchannel *ch) +{ + if (IFCHANNEL_NOINFO(ch)) { + + /* In NOINFO state, timers should have been cleared */ + zassert(!ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + zassert(!ch->t_ifassert_timer); + + pim_ifchannel_delete(ch); + } +} + +void pim_ifchannel_ifjoin_switch(const char *caller, + struct pim_ifchannel *ch, + enum pim_ifjoin_state new_state) +{ + enum pim_ifjoin_state old_state = ch->ifjoin_state; + + if (old_state == new_state) { + zlog_debug("%s calledby %s: non-transition on state %d (%s)", + __PRETTY_FUNCTION__, caller, new_state, + pim_ifchannel_ifjoin_name(new_state)); + return; + } + + zassert(old_state != new_state); + + ch->ifjoin_state = new_state; + + /* Transition to/from NOINFO ? */ + if ( + (old_state == PIM_IFJOIN_NOINFO) + || + (new_state == PIM_IFJOIN_NOINFO) + ) { + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s", + ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"), + src_str, grp_str, ch->interface->name); + } + + /* + Record uptime of state transition to/from NOINFO + */ + ch->ifjoin_creation = pim_time_monotonic_sec(); + + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); + } +} + +const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state) +{ + switch (ifjoin_state) { + case PIM_IFJOIN_NOINFO: return "NOINFO"; + case PIM_IFJOIN_JOIN: return "JOIN"; + case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + } + + return "ifjoin_bad_state"; +} + +const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state) +{ + switch (ifassert_state) { + case PIM_IFASSERT_NOINFO: return "NOINFO"; + case PIM_IFASSERT_I_AM_WINNER: return "WINNER"; + case PIM_IFASSERT_I_AM_LOSER: return "LOSER"; + } + + return "ifassert_bad_state"; +} + +/* + RFC 4601: 4.6.5. Assert State Macros + + AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I) + defaults to Infinity when in the NoInfo state. +*/ +void reset_ifassert_state(struct pim_ifchannel *ch) +{ + THREAD_OFF(ch->t_ifassert_timer); + + pim_ifassert_winner_set(ch, + PIM_IFASSERT_NOINFO, + qpim_inaddr_any, + qpim_infinite_assert_metric); +} + +static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + struct pim_upstream *up; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + up = pim_upstream_add(source_addr, group_addr); + if (!up) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return 0; + } + + ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); + if (!ch) { + zlog_err("%s: PIM XMALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*ch)); + return 0; + } + + ch->flags = 0; + ch->upstream = up; + ch->interface = ifp; + ch->source_addr = source_addr; + ch->group_addr = group_addr; + ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; + + ch->ifjoin_state = PIM_IFJOIN_NOINFO; + ch->t_ifjoin_expiry_timer = 0; + ch->t_ifjoin_prune_pending_timer = 0; + ch->ifjoin_creation = 0; + + /* Assert state */ + ch->t_ifassert_timer = 0; + reset_ifassert_state(ch); + if (pim_macro_ch_could_assert_eval(ch)) + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + else + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (pim_macro_assert_tracking_desired_eval(ch)) + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + else + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); + + /* Attach to list */ + listnode_add(pim_ifp->pim_ifchannel_list, ch); + + zassert(IFCHANNEL_NOINFO(ch)); + + return ch; +} + +struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + zassert(ifp); + + pim_ifp = ifp->info; + + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + ifp->name); + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + if ( + (source_addr.s_addr == ch->source_addr.s_addr) && + (group_addr.s_addr == ch->group_addr.s_addr) + ) { + return ch; + } + } + + return 0; +} + +static void ifmembership_set(struct pim_ifchannel *ch, + enum pim_ifmembership membership) +{ + if (ch->local_ifmembership == membership) + return; + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", + ch->interface->name); + } + + ch->local_ifmembership = membership; + + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); +} + + +void pim_ifchannel_membership_clear(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + } +} + +void pim_ifchannel_delete_on_noinfo(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + delete_on_noinfo(ch); + } +} + +struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + char src_str[100]; + char grp_str[100]; + + ch = pim_ifchannel_find(ifp, source_addr, group_addr); + if (ch) + return ch; + + ch = pim_ifchannel_new(ifp, source_addr, group_addr); + if (ch) + return ch; + + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + + return 0; +} + +static void ifjoin_to_noinfo(struct pim_ifchannel *ch) +{ + pim_forward_stop(ch); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + delete_on_noinfo(ch); +} + +static int on_ifjoin_expiry_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ch->t_ifjoin_expiry_timer = 0; + + zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN); + + ifjoin_to_noinfo(ch); + /* ch may have been deleted */ + + return 0; +} + +static void prune_echo(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_interface *pim_ifp; + struct in_addr neigh_dst_addr; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + neigh_dst_addr = pim_ifp->primary_address; + + if (PIM_DEBUG_PIM_EVENTS) { + char source_str[100]; + char group_str[100]; + char neigh_dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str)); + zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s", + __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name); + } + + pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr, + 0 /* boolean: send_join=false (prune) */); +} + +static int on_ifjoin_prune_pending_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + int send_prune_echo; /* boolean */ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr ch_source; + struct in_addr ch_group; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ch->t_ifjoin_prune_pending_timer = 0; + + zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); + + /* Send PruneEcho(S,G) ? */ + ifp = ch->interface; + pim_ifp = ifp->info; + send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); + + /* Save (S,G) */ + ch_source = ch->source_addr; + ch_group = ch->group_addr; + + ifjoin_to_noinfo(ch); + /* from here ch may have been deleted */ + + if (send_prune_echo) + prune_echo(ifp, ch_source, ch_group); + + return 0; +} + +static void check_recv_upstream(int is_join, + struct interface *recv_ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + int holdtime) +{ + struct pim_upstream *up; + + /* Upstream (S,G) in Joined state ? */ + up = pim_upstream_find(source_addr, group_addr); + if (!up) + return; + if (up->join_state != PIM_UPSTREAM_JOINED) + return; + + /* Upstream (S,G) in Joined state */ + + if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + /* RPF'(S,G) not found */ + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s %s: RPF'(%s,%s) not found", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str); + return; + } + + /* upstream directed to RPF'(S,G) ? */ + if (upstream.s_addr != up->rpf.rpf_addr.s_addr) { + char src_str[100]; + char grp_str[100]; + char up_str[100]; + char rpf_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + up_str, rpf_str, recv_ifp->name); + return; + } + /* upstream directed to RPF'(S,G) */ + + if (is_join) { + /* Join(S,G) to RPF'(S,G) */ + pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime); + return; + } + + /* Prune to RPF'(S,G) */ + + if (source_flags & PIM_RPT_BIT_MASK) { + if (source_flags & PIM_WILDCARD_BIT_MASK) { + /* Prune(*,G) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", + up, up->rpf.rpf_addr); + return; + } + + /* Prune(S,G,rpt) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", + up, up->rpf.rpf_addr); + return; + } + + /* Prune(S,G) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up, + up->rpf.rpf_addr); +} + +static int nonlocal_upstream(int is_join, + struct interface *recv_ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_interface *recv_pim_ifp; + int is_local; /* boolean */ + + recv_pim_ifp = recv_ifp->info; + zassert(recv_pim_ifp); + + is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr); + + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s", + __PRETTY_FUNCTION__, + is_join ? "join" : "prune", + src_str, grp_str, + is_local ? "local" : "non-local", + up_str, recv_ifp->name); + } + + if (is_local) + return 0; + + /* + Since recv upstream addr was not directed to our primary + address, check if we should react to it in any way. + */ + check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr, + source_flags, holdtime); + + return 1; /* non-local */ +} + +void pim_ifchannel_join_add(struct interface *ifp, + struct in_addr neigh_addr, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + if (nonlocal_upstream(1 /* join */, ifp, upstream, + source_addr, group_addr, source_flags, holdtime)) { + return; + } + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) + return; + + /* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + Transitions from "I am Assert Loser" State + + Receive Join(S,G) on Interface I + + We receive a Join(S,G) that has the Upstream Neighbor Address + field set to my primary IP address on interface I. The action is + to transition to NoInfo state, delete this (S,G) assert state + (Actions A5 below), and allow the normal PIM Join/Prune mechanisms + to operate. + + Notice: The nonlocal_upstream() test above ensures the upstream + address of the join message is our primary address. + */ + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, neigh_str, ifp->name); + + assert_action_a5(ch); + } + + pim_ifp = ifp->info; + zassert(pim_ifp); + + switch (ch->ifjoin_state) { + case PIM_IFJOIN_NOINFO: + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + if (pim_macro_chisin_oiflist(ch)) { + pim_forward_start(ch); + } + break; + case PIM_IFJOIN_JOIN: + zassert(!ch->t_ifjoin_prune_pending_timer); + + /* + In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a + previously received join message with holdtime=0xFFFF. + */ + if (ch->t_ifjoin_expiry_timer) { + unsigned long remain = + thread_timer_remain_second(ch->t_ifjoin_expiry_timer); + if (remain > holdtime) { + /* + RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages + + Transitions from Join State + + The (S,G) downstream state machine on interface I remains in + Join state, and the Expiry Timer (ET) is restarted, set to + maximum of its current value and the HoldTime from the + triggering Join/Prune message. + + Conclusion: Do not change the ET if the current value is + higher than the received join holdtime. + */ + return; + } + } + THREAD_OFF(ch->t_ifjoin_expiry_timer); + break; + case PIM_IFJOIN_PRUNE_PENDING: + zassert(!ch->t_ifjoin_expiry_timer); + zassert(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + break; + } + + zassert(!IFCHANNEL_NOINFO(ch)); + + if (holdtime != 0xFFFF) { + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } +} + +void pim_ifchannel_prune(struct interface *ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_ifchannel *ch; + int jp_override_interval_msec; + + if (nonlocal_upstream(0 /* prune */, ifp, upstream, + source_addr, group_addr, source_flags, holdtime)) { + return; + } + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) + return; + + switch (ch->ifjoin_state) { + case PIM_IFJOIN_NOINFO: + case PIM_IFJOIN_PRUNE_PENDING: + /* nothing to do */ + break; + case PIM_IFJOIN_JOIN: + { + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + + zassert(ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + + THREAD_OFF(ch->t_ifjoin_expiry_timer); + + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); + + if (listcount(pim_ifp->pim_neighbor_list) > 1) { + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + } + else { + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + } + + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + + zassert(!ch->t_ifjoin_expiry_timer); + zassert(ch->t_ifjoin_prune_pending_timer); + } + break; + } + +} + +void pim_ifchannel_local_membership_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* PIM enabled on interface? */ + pim_ifp = ifp->info; + if (!pim_ifp) + return; + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) { + return; + } + + ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); + + zassert(!IFCHANNEL_NOINFO(ch)); +} + +void pim_ifchannel_local_membership_del(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* PIM enabled on interface? */ + pim_ifp = ifp->info; + if (!pim_ifp) + return; + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + + ch = pim_ifchannel_find(ifp, source_addr, group_addr); + if (!ch) + return; + + ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + + delete_on_noinfo(ch); +} + +void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) +{ + int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)); + int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch)); + + if (new_couldassert == old_couldassert) + return; + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_couldassert, new_couldassert); + } + + if (new_couldassert) { + /* CouldAssert(S,G,I) switched from FALSE to TRUE */ + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + } + else { + /* CouldAssert(S,G,I) switched from TRUE to FALSE */ + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) { + assert_action_a4(ch); + } + } + + pim_ifchannel_update_my_assert_metric(ch); +} + +/* + my_assert_metric may be affected by: + + CouldAssert(S,G) + pim_ifp->primary_address + rpf->source_nexthop.mrib_metric_preference; + rpf->source_nexthop.mrib_route_metric; + */ +void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) +{ + struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch); + + if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric)) + return; + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + char old_addr_str[100]; + char new_addr_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); + pim_inet4_dump("", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); + zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + ch->ifassert_my_metric.rpt_bit_flag, + ch->ifassert_my_metric.metric_preference, + ch->ifassert_my_metric.route_metric, + old_addr_str, + my_metric_new.rpt_bit_flag, + my_metric_new.metric_preference, + my_metric_new.route_metric, + new_addr_str); + } + + ch->ifassert_my_metric = my_metric_new; + + if (pim_assert_metric_better(&ch->ifassert_my_metric, + &ch->ifassert_winner_metric)) { + assert_action_a5(ch); + } +} + +void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) +{ + int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)); + int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch)); + + if (new_atd == old_atd) + return; + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_atd, new_atd); + } + + if (new_atd) { + /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */ + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + } + else { + /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */ + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + assert_action_a5(ch); + } + } +} diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h new file mode 100644 index 00000000..e6f1c294 --- /dev/null +++ b/pimd/pim_ifchannel.h @@ -0,0 +1,145 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IFCHANNEL_H +#define PIM_IFCHANNEL_H + +#include + +#include "if.h" + +#include "pim_upstream.h" + +enum pim_ifmembership { + PIM_IFMEMBERSHIP_NOINFO, + PIM_IFMEMBERSHIP_INCLUDE +}; + +enum pim_ifjoin_state { + PIM_IFJOIN_NOINFO, + PIM_IFJOIN_JOIN, + PIM_IFJOIN_PRUNE_PENDING +}; + +enum pim_ifassert_state { + PIM_IFASSERT_NOINFO, + PIM_IFASSERT_I_AM_WINNER, + PIM_IFASSERT_I_AM_LOSER +}; + +struct pim_assert_metric { + uint32_t rpt_bit_flag; + uint32_t metric_preference; + uint32_t route_metric; + struct in_addr ip_address; /* neighbor router that sourced the Assert message */ +}; + +/* + Flag to detect change in CouldAssert(S,G,I) +*/ +#define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0) +#define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT) +/* + Flag to detect change in AssertTrackingDesired(S,G,I) +*/ +#define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1) +#define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) + +/* + Per-interface (S,G) state +*/ +struct pim_ifchannel { + struct in_addr source_addr; /* (S,G) source key */ + struct in_addr group_addr; /* (S,G) group key */ + struct interface *interface; /* backpointer to interface */ + uint32_t flags; + + /* IGMPv3 determined interface has local members for (S,G) ? */ + enum pim_ifmembership local_ifmembership; + + /* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */ + enum pim_ifjoin_state ifjoin_state; + struct thread *t_ifjoin_expiry_timer; + struct thread *t_ifjoin_prune_pending_timer; + int64_t ifjoin_creation; /* Record uptime of ifjoin state */ + + /* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */ + enum pim_ifassert_state ifassert_state; + struct thread *t_ifassert_timer; + struct in_addr ifassert_winner; + struct pim_assert_metric ifassert_winner_metric; + int64_t ifassert_creation; /* Record uptime of ifassert state */ + struct pim_assert_metric ifassert_my_metric; + + /* Upstream (S,G) state */ + struct pim_upstream *upstream; +}; + +void pim_ifchannel_free(struct pim_ifchannel *ch); +void pim_ifchannel_delete(struct pim_ifchannel *ch); +void pim_ifchannel_membership_clear(struct interface *ifp); +void pim_ifchannel_delete_on_noinfo(struct interface *ifp); +struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +void pim_ifchannel_join_add(struct interface *ifp, + struct in_addr neigh_addr, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime); +void pim_ifchannel_prune(struct interface *ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime); +void pim_ifchannel_local_membership_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +void pim_ifchannel_local_membership_del(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); + +void pim_ifchannel_ifjoin_switch(const char *caller, + struct pim_ifchannel *ch, + enum pim_ifjoin_state new_state); +const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state); +const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state); + +int pim_ifchannel_isin_oiflist(struct pim_ifchannel *ch); + +void reset_ifassert_state(struct pim_ifchannel *ch); + +void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch); +void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch); +void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch); + +#endif /* PIM_IFCHANNEL_H */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c new file mode 100644 index 00000000..4fd3edcb --- /dev/null +++ b/pimd/pim_igmp.c @@ -0,0 +1,1433 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "memory.h" + +#include "pimd.h" +#include "pim_igmp.h" +#include "pim_igmpv3.h" +#include "pim_iface.h" +#include "pim_sock.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_time.h" +#include "pim_zebra.h" + +#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1) +#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4) +#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5) +#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6) + +static void group_timer_off(struct igmp_group *group); + +static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr); + +static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_options) +{ + int fd; + int join = 0; + struct in_addr group; + + fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */); + if (fd < 0) + return -1; + + if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) { + if (inet_aton(PIM_ALL_ROUTERS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) + ++join; + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_ROUTERS, errno, safe_strerror(errno)); + } + } + + /* + IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1 + IGMP routers must receive general queries for querier election. + */ + if (inet_aton(PIM_ALL_SYSTEMS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) + ++join; + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); + } + + if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) { + ++join; + } + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); + } + + if (!join) { + zlog_err("IGMP socket fd=%d could not join any group on interface address %s", + fd, inet_ntoa(ifaddr)); + close(fd); + fd = -1; + } + + return fd; +} + +#undef IGMP_SOCK_DUMP + +#ifdef IGMP_SOCK_DUMP +static void igmp_sock_dump(array_t *igmp_sock_array) +{ + int size = array_size(igmp_sock_array); + for (int i = 0; i < size; ++i) { + + struct igmp_sock *igmp = array_get(igmp_sock_array, i); + + zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", + __FILE__, __PRETTY_FUNCTION__, + i, size, + inet_ntoa(igmp->ifaddr), + igmp->fd); + } +} +#endif + +struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, + struct in_addr ifaddr) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + +#ifdef IGMP_SOCK_DUMP + igmp_sock_dump(igmp_sock_list); +#endif + + for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) + if (ifaddr.s_addr == igmp->ifaddr.s_addr) + return igmp; + + return 0; +} + +struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, + int fd) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) + if (fd == igmp->fd) + return igmp; + + return 0; +} + +static int pim_igmp_other_querier_expire(struct thread *t) +{ + struct igmp_sock *igmp; + + zassert(t); + igmp = THREAD_ARG(t); + zassert(igmp); + + zassert(igmp->t_other_querier_timer); + zassert(!igmp->t_igmp_query_timer); + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: Querier %s resuming", + __PRETTY_FUNCTION__, + ifaddr_str); + } + + igmp->t_other_querier_timer = 0; + + /* + We are the current querier, then + re-start sending general queries. + */ + pim_igmp_general_query_on(igmp); + + return 0; +} + +void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp) +{ + long other_querier_present_interval_msec; + struct pim_interface *pim_ifp; + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + if (igmp->t_other_querier_timer) { + /* + There is other querier present already, + then reset the other-querier-present timer. + */ + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present", + ifaddr_str); + } + + THREAD_OFF(igmp->t_other_querier_timer); + zassert(!igmp->t_other_querier_timer); + } + else { + /* + We are the current querier, then stop sending general queries: + igmp->t_igmp_query_timer = 0; + */ + pim_igmp_general_query_off(igmp); + } + + /* + Since this socket is starting the other-querier-present timer, + there should not be periodic query timer for this socket. + */ + zassert(!igmp->t_igmp_query_timer); + + /* + RFC 3376: 8.5. Other Querier Present Interval + + The Other Querier Present Interval is the length of time that must + pass before a multicast router decides that there is no longer + another multicast router which should be the querier. This value + MUST be ((the Robustness Variable) times (the Query Interval)) plus + (one half of one Query Response Interval). + + other_querier_present_interval_msec = \ + igmp->querier_robustness_variable * \ + 1000 * igmp->querier_query_interval + \ + 100 * (pim_ifp->query_max_response_time_dsec >> 1); + */ + other_querier_present_interval_msec = + PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present", + ifaddr_str, + other_querier_present_interval_msec / 1000, + other_querier_present_interval_msec % 1000); + } + + THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer, + pim_igmp_other_querier_expire, + igmp, other_querier_present_interval_msec); +} + +void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_other_querier_timer) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s", + ifaddr_str, igmp->fd, igmp->interface->name); + } + } + THREAD_OFF(igmp->t_other_querier_timer); + zassert(!igmp->t_other_querier_timer); +} + +static int recv_igmp_query(struct igmp_sock *igmp, int query_version, + int max_resp_code, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + uint8_t resv_s_qrv = 0; + uint8_t s_flag = 0; + uint8_t qrv = 0; + struct in_addr group_addr; + uint16_t recv_checksum; + uint16_t checksum; + int i; + + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + + ifp = igmp->interface; + pim_ifp = ifp->info; + + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); + + /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; + + checksum = in_cksum(igmp_msg, igmp_msg_len); + if (checksum != recv_checksum) { + zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x", + query_version, from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s", + query_version, from_str, ifp->name, + igmp_msg_len, checksum, group_str); + } + + /* + RFC 3376: 6.6.2. Querier Election + + When a router receives a query with a lower IP address, it sets + the Other-Querier-Present timer to Other Querier Present Interval + and ceases to send queries on the network if it was the previously + elected querier. + */ + if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) { + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)", + ifp->name, + ifaddr_str, ntohl(igmp->ifaddr.s_addr), + from_str, ntohl(from.s_addr)); + } + + pim_igmp_other_querier_timer_on(igmp); + } + + if (query_version == 3) { + /* + RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + + Routers adopt the QRV value from the most recently received Query + as their own [Robustness Variable] value, unless that most + recently received QRV was zero, in which case the receivers use + the default [Robustness Variable] value specified in section 8.1 + or a statically configured value. + */ + resv_s_qrv = igmp_msg[8]; + qrv = 7 & resv_s_qrv; + igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + } + + /* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + Multicast routers that are not the current querier adopt the QQI + value from the most recently received Query as their own [Query + Interval] value, unless that most recently received QQI was zero, + in which case the receiving routers use the default. + */ + if (igmp->t_other_querier_timer && query_version == 3) { + /* other querier present */ + uint8_t qqic; + uint16_t qqi; + qqic = igmp_msg[9]; + qqi = igmp_msg_decode8to16(qqic); + igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval; + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)", + ifaddr_str, + qqi ? "recv-non-default" : "default", + igmp->querier_query_interval, + qqic, + from_str); + } + } + + /* + RFC 3376: 6.6.1. Timer Updates + + When a router sends or receives a query with a clear Suppress + Router-Side Processing flag, it must update its timers to reflect + the correct timeout values for the group or sources being queried. + + General queries don't trigger timer update. + */ + if (query_version == 3) { + s_flag = (1 << 3) & resv_s_qrv; + } + else { + /* Neither V1 nor V2 have this field. Pimd should really go into + * a compatibility mode here and run as V2 (or V1) but it doesn't + * so for now, lets just set the flag to suppress these timer updates. + */ + s_flag = 1; + } + + if (!s_flag) { + /* s_flag is clear */ + + if (PIM_INADDR_IS_ANY(group_addr)) { + /* this is a general query */ + + /* log that general query should have the s_flag set */ + zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear", + query_version, from_str, ifp->name); + } + else { + struct igmp_group *group; + + /* this is a non-general query: perform timer updates */ + + group = find_group_by_addr(igmp, group_addr); + if (group) { + int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET)); + + /* + RFC 3376: 6.6.1. Timer Updates + Query Q(G,A): Source Timer for sources in A are lowered to LMQT + Query Q(G): Group Timer is lowered to LMQT + */ + if (recv_num_sources < 1) { + /* Query Q(G): Group Timer is lowered to LMQT */ + + igmp_group_timer_lower_to_lmqt(group); + } + else { + /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */ + + /* Scan sources in query and lower their timers to LMQT */ + struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); + for (i = 0; i < recv_num_sources; ++i) { + //struct in_addr src_addr = sources[i]; + //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); + struct in_addr src_addr; + struct igmp_source *src; + memcpy(&src_addr, sources + i, sizeof(struct in_addr)); + src = igmp_find_source_by_addr(group, src_addr); + if (src) { + igmp_source_timer_lower_to_lmqt(src); + } + } + } + + } + else { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update", + query_version, from_str, ifp->name, group_str); + } + } + } /* s_flag is clear: timer updates */ + + return 0; +} + +static int igmp_v3_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + uint16_t recv_checksum; + uint16_t checksum; + int num_groups; + uint8_t *group_record; + uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len; + struct interface *ifp = igmp->interface; + int i; + + if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { + zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE); + return -1; + } + + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); + + /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; + + checksum = in_cksum(igmp_msg, igmp_msg_len); + if (checksum != recv_checksum) { + zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); + if (num_groups < 1) { + zlog_warn("Recv IGMP report v3 from %s on %s: missing group records", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d", + from_str, ifp->name, igmp_msg_len, checksum, num_groups); + } + + group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; + + /* Scan groups */ + for (i = 0; i < num_groups; ++i) { + struct in_addr rec_group; + uint8_t *sources; + uint8_t *src; + int rec_type; + int rec_auxdatalen; + int rec_num_sources; + int j; + + if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end", + from_str, ifp->name); + return -1; + } + + rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET]; + rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; + rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); + + //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET); + memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr)); + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", + from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group)); + } + + /* Scan sources */ + + sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET; + + for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { + + if ((src + 4) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char src_str[200]; + + if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str))) + sprintf(src_str, ""); + + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", + from_str, ifp->name, i, inet_ntoa(rec_group), src_str); + } + } /* for (sources) */ + + switch (rec_type) { + case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE: + igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: + igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE: + igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: + igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES: + igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES: + igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + default: + zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d", + from_str, ifp->name, rec_type); + } + + group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2); + + } /* for (group records) */ + + return 0; +} + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr from) +{ + if (PIM_DEBUG_IGMP_TRACE) { + char from_str[100]; + pim_inet4_dump("", from, from_str, sizeof(from_str)); + zlog_debug("%s: from %s on %s", + label, from_str, ifp->name); + } +} + +static int igmp_v2_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + struct in_addr group_addr; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return -1; + } + + group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec(); + + return 0; +} + +static int igmp_v2_leave(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + return 0; +} + +static int igmp_v1_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + struct in_addr group_addr; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return -1; + } + + group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec(); + + return 0; +} + +int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) +{ + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + char *igmp_msg; + int igmp_msg_len; + int msg_type; + char from_str[100]; + char to_str[100]; + + if (len < sizeof(*ip_hdr)) { + zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", + len, sizeof(*ip_hdr)); + return -1; + } + + ip_hdr = (struct ip *) buf; + + pim_inet4_dump("", ip_hdr->ip_src, from_str , sizeof(from_str)); + pim_inet4_dump("", ip_hdr->ip_dst, to_str , sizeof(to_str)); + + ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", + from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p); + } + + if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) { + zlog_warn("IP packet protocol=%d is not IGMP=%d", + ip_hdr->ip_p, PIM_IP_PROTO_IGMP); + return -1; + } + + if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { + zlog_warn("IP packet header size=%zu shorter than minimum=%d", + ip_hlen, PIM_IP_HEADER_MIN_LEN); + return -1; + } + if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { + zlog_warn("IP packet header size=%zu greater than maximum=%d", + ip_hlen, PIM_IP_HEADER_MAX_LEN); + return -1; + } + + igmp_msg = buf + ip_hlen; + msg_type = *igmp_msg; + igmp_msg_len = len - ip_hlen; + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d", + from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type, + igmp_msg_len); + } + + if (igmp_msg_len < PIM_IGMP_MIN_LEN) { + zlog_warn("IGMP message size=%d shorter than minimum=%d", + igmp_msg_len, PIM_IGMP_MIN_LEN); + return -1; + } + + switch (msg_type) { + case PIM_IGMP_MEMBERSHIP_QUERY: + { + int max_resp_code = igmp_msg[1]; + int query_version; + + /* + RFC 3376: 7.1. Query Version Distinctions + IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero + IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero + IGMPv3 Query: length >= 12 octets + */ + + if (igmp_msg_len == 8) { + query_version = max_resp_code ? 2 : 1; + } + else if (igmp_msg_len >= 12) { + query_version = 3; + } + else { + zlog_warn("Unknown IGMP query version"); + return -1; + } + + return recv_igmp_query(igmp, query_version, max_resp_code, + ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + } + + case PIM_IGMP_V3_MEMBERSHIP_REPORT: + return igmp_v3_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V2_MEMBERSHIP_REPORT: + return igmp_v2_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V1_MEMBERSHIP_REPORT: + return igmp_v1_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V2_LEAVE_GROUP: + return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + } + + zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); + + return -1; +} + +static int pim_igmp_general_query(struct thread *t); + +void pim_igmp_general_query_on(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + int startup_mode; + int query_interval; + + zassert(igmp); + zassert(igmp->interface); + + /* + Since this socket is starting as querier, + there should not exist a timer for other-querier-present. + */ + zassert(!igmp->t_other_querier_timer); + pim_ifp = igmp->interface->info; + zassert(pim_ifp); + + /* + RFC 3376: 8.6. Startup Query Interval + + The Startup Query Interval is the interval between General Queries + sent by a Querier on startup. Default: 1/4 the Query Interval. + */ + startup_mode = igmp->startup_query_count > 0; + if (startup_mode) { + --igmp->startup_query_count; + + /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */ + query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + } + else { + query_interval = igmp->querier_query_interval; + } + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d", + ifaddr_str, + query_interval, + startup_mode ? "startup" : "non-startup", + igmp->fd); + } + igmp->t_igmp_query_timer = 0; + zassert(!igmp->t_igmp_query_timer); + THREAD_TIMER_ON(master, igmp->t_igmp_query_timer, + pim_igmp_general_query, + igmp, query_interval); +} + +void pim_igmp_general_query_off(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_igmp_query_timer) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s", + ifaddr_str, igmp->fd, igmp->interface->name); + } + } + THREAD_OFF(igmp->t_igmp_query_timer); + zassert(!igmp->t_igmp_query_timer); +} + +/* Issue IGMP general query */ +static int pim_igmp_general_query(struct thread *t) +{ + char query_buf[PIM_IGMP_BUFSIZE_WRITE]; + struct igmp_sock *igmp; + struct in_addr dst_addr; + struct in_addr group_addr; + struct pim_interface *pim_ifp; + + zassert(t); + + igmp = THREAD_ARG(t); + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + In IGMPv3, General Queries are sent with an IP destination address + of 224.0.0.1, the all-systems multicast address. Group-Specific + and Group-and-Source-Specific Queries are sent with an IP + destination address equal to the multicast address of interest. + */ + + dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + group_addr.s_addr = PIM_NET_INADDR_ANY; + + if (PIM_DEBUG_IGMP_TRACE) { + char querier_str[100]; + char dst_str[100]; + pim_inet4_dump("", igmp->ifaddr, querier_str, + sizeof(querier_str)); + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + zlog_debug("Querier %s issuing IGMP general query to %s on %s", + querier_str, dst_str, igmp->interface->name); + } + + pim_igmp_send_membership_query(0 /* igmp_group */, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources */, + dst_addr, + group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 1 /* s_flag: always set for general queries */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + pim_igmp_general_query_on(igmp); + + return 0; +} + +static int pim_igmp_read(struct thread *t); + +static void igmp_read_on(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Scheduling READ event on IGMP socket fd=%d", + igmp->fd); + } + igmp->t_igmp_read = 0; + zassert(!igmp->t_igmp_read); + THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd); +} + +static int pim_igmp_read(struct thread *t) +{ + struct igmp_sock *igmp; + int fd; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + uint8_t buf[PIM_IGMP_BUFSIZE_READ]; + int len; + int ifindex = -1; + int result = -1; /* defaults to bad */ + + zassert(t); + + igmp = THREAD_ARG(t); + + zassert(igmp); + + fd = THREAD_FD(t); + + zassert(fd == igmp->fd); + + len = pim_socket_recvfromto(fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + goto done; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char from_str[100]; + char to_str[100]; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", + len, from_str, to_str, fd, ifindex, igmp->interface->ifindex); + } + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + /* ifindex sanity check */ + if (ifindex != (int) igmp->interface->ifindex) { + char from_str[100]; + char to_str[100]; + struct interface *ifp; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + ifp = if_lookup_by_index(ifindex); + if (ifp) { + zassert(ifindex == (int) ifp->ifindex); + } + +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", + from_str, to_str, fd, + ifindex, ifp ? ifp->name : "", + igmp->interface->ifindex, igmp->interface->name); +#endif + goto done; + } +#endif + + if (pim_igmp_packet(igmp, (char *)buf, len)) { + goto done; + } + + result = 0; /* good */ + + done: + igmp_read_on(igmp); + + return result; +} + +static void sock_close(struct igmp_sock *igmp) +{ + pim_igmp_other_querier_timer_off(igmp); + pim_igmp_general_query_off(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_igmp_read) { + zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s", + inet_ntoa(igmp->ifaddr), igmp->fd, + igmp->interface->name); + } + } + THREAD_OFF(igmp->t_igmp_read); + zassert(!igmp->t_igmp_read); + + if (close(igmp->fd)) { + zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", + inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name, + errno, safe_strerror(errno)); + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Deleted IGMP socket %s fd=%d on interface %s", + inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name); + } +} + +void igmp_startup_mode_on(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = igmp->interface->info; + + /* + RFC 3376: 8.7. Startup Query Count + + The Startup Query Count is the number of Queries sent out on + startup, separated by the Startup Query Interval. Default: the + Robustness Variable. + */ + igmp->startup_query_count = igmp->querier_robustness_variable; + + /* + Since we're (re)starting, reset QQI to default Query Interval + */ + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; +} + +static void igmp_group_free(struct igmp_group *group) +{ + zassert(!group->t_group_query_retransmit_timer); + zassert(!group->t_group_timer); + zassert(group->group_source_list); + zassert(!listcount(group->group_source_list)); + + list_free(group->group_source_list); + + XFREE(MTYPE_PIM_IGMP_GROUP, group); +} + +static void igmp_group_delete(struct igmp_group *group) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Deleting IGMP group %s from socket %d interface %s", + group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + } + + for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) { + igmp_source_delete(src); + } + + if (group->t_group_query_retransmit_timer) { + THREAD_OFF(group->t_group_query_retransmit_timer); + zassert(!group->t_group_query_retransmit_timer); + } + + group_timer_off(group); + listnode_delete(group->group_igmp_sock->igmp_group_list, group); + igmp_group_free(group); +} + +void igmp_group_delete_empty_include(struct igmp_group *group) +{ + zassert(!group->group_filtermode_isexcl); + zassert(!listcount(group->group_source_list)); + + igmp_group_delete(group); +} + +void igmp_sock_free(struct igmp_sock *igmp) +{ + zassert(!igmp->t_igmp_read); + zassert(!igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + zassert(igmp->igmp_group_list); + zassert(!listcount(igmp->igmp_group_list)); + + list_free(igmp->igmp_group_list); + + XFREE(MTYPE_PIM_IGMP_SOCKET, igmp); +} + +void igmp_sock_delete(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + struct listnode *grp_node; + struct listnode *grp_nextnode; + struct igmp_group *grp; + + for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) { + igmp_group_delete(grp); + } + + sock_close(igmp); + + pim_ifp = igmp->interface->info; + + listnode_delete(pim_ifp->igmp_socket_list, igmp); + + igmp_sock_free(igmp); +} + +static struct igmp_sock *igmp_sock_new(int fd, + struct in_addr ifaddr, + struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s", + fd, inet_ntoa(ifaddr), ifp->name); + } + + igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); + if (!igmp) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + + igmp->igmp_group_list = list_new(); + if (!igmp->igmp_group_list) { + zlog_err("%s %s: failure: igmp_group_list = list_new()", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free; + + igmp->fd = fd; + igmp->interface = ifp; + igmp->ifaddr = ifaddr; + igmp->t_igmp_read = 0; + igmp->t_igmp_query_timer = 0; + igmp->t_other_querier_timer = 0; /* no other querier present */ + igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable; + igmp->sock_creation = pim_time_monotonic_sec(); + + /* + igmp_startup_mode_on() will reset QQI: + + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; + */ + igmp_startup_mode_on(igmp); + + igmp_read_on(igmp); + pim_igmp_general_query_on(igmp); + + return igmp; +} + +struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, + struct in_addr ifaddr, + struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + int fd; + + pim_ifp = ifp->info; + + fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options); + if (fd < 0) { + zlog_warn("Could not open IGMP socket for %s on %s", + inet_ntoa(ifaddr), ifp->name); + return 0; + } + + igmp = igmp_sock_new(fd, ifaddr, ifp); + if (!igmp) { + zlog_err("%s %s: igmp_sock_new() failure", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return 0; + } + + listnode_add(igmp_sock_list, igmp); + +#ifdef IGMP_SOCK_DUMP + igmp_sock_dump(igmp_sock_array); +#endif + + return igmp; +} + +/* + RFC 3376: 6.5. Switching Router Filter-Modes + + When a router's filter-mode for a group is EXCLUDE and the group + timer expires, the router filter-mode for the group transitions to + INCLUDE. + + A router uses source records with running source timers as its state + for the switch to a filter-mode of INCLUDE. If there are any source + records with source timers greater than zero (i.e., requested to be + forwarded), a router switches to filter-mode of INCLUDE using those + source records. Source records whose timers are zero (from the + previous EXCLUDE mode) are deleted. + */ +static int igmp_group_timer(struct thread *t) +{ + struct igmp_group *group; + + zassert(t); + group = THREAD_ARG(t); + zassert(group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: Timer for group %s on interface %s", + __PRETTY_FUNCTION__, + group_str, group->group_igmp_sock->interface->name); + } + + zassert(group->group_filtermode_isexcl); + + group->t_group_timer = 0; + group->group_filtermode_isexcl = 0; + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + igmp_source_delete_expired(group->group_source_list); + + zassert(!group->t_group_timer); + zassert(!group->group_filtermode_isexcl); + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + If there are no more source records for the group, delete group + record. + */ + if (listcount(group->group_source_list) < 1) { + igmp_group_delete_empty_include(group); + } + + return 0; +} + +static void group_timer_off(struct igmp_group *group) +{ + if (!group->t_group_timer) + return; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Cancelling TIMER event for group %s on %s", + group_str, group->group_igmp_sock->interface->name); + } + + THREAD_OFF(group->t_group_timer); + zassert(!group->t_group_timer); +} + +void igmp_group_timer_on(struct igmp_group *group, + long interval_msec, const char *ifname) +{ + group_timer_off(group); + + if (PIM_DEBUG_IGMP_EVENTS) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s", + interval_msec / 1000, + interval_msec % 1000, + group_str, ifname); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(group->group_filtermode_isexcl); + + THREAD_TIMER_MSEC_ON(master, group->t_group_timer, + igmp_group_timer, + group, interval_msec); +} + +static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr) +{ + struct igmp_group *group; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group)) + if (group_addr.s_addr == group->group_addr.s_addr) + return group; + + return 0; +} + +struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr, + const char *ifname) +{ + struct igmp_group *group; + + group = find_group_by_addr(igmp, group_addr); + if (group) { + return group; + } + + /* + Non-existant group is created as INCLUDE {empty}: + + RFC 3376 - 5.1. Action on Change of Interface State + + If no interface state existed for that multicast address before + the change (i.e., the change consisted of creating a new + per-interface record), or if no state exists after the change + (i.e., the change consisted of deleting a per-interface record), + then the "non-existent" state is considered to have a filter mode + of INCLUDE and an empty source list. + */ + + group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); + if (!group) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; /* error, not found, could not create */ + } + + group->group_source_list = list_new(); + if (!group->group_source_list) { + zlog_warn("%s %s: list_new() failure", + __FILE__, __PRETTY_FUNCTION__); + XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */ + return 0; /* error, not found, could not initialize */ + } + group->group_source_list->del = (void (*)(void *)) igmp_source_free; + + group->t_group_timer = 0; + group->t_group_query_retransmit_timer = 0; + group->group_specific_query_retransmit_count = 0; + group->group_addr = group_addr; + group->group_igmp_sock = igmp; + group->last_igmp_v1_report_dsec = -1; + group->last_igmp_v2_report_dsec = -1; + group->group_creation = pim_time_monotonic_sec(); + + /* initialize new group as INCLUDE {empty} */ + group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ + + listnode_add(igmp->igmp_group_list, group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Creating new IGMP group %s on socket %d interface %s", + group_str, group->group_igmp_sock->fd, ifname); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */ + zassert(!group->t_group_timer); /* group timer == 0 */ + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + return group; +} diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h new file mode 100644 index 00000000..d45f223b --- /dev/null +++ b/pimd/pim_igmp.h @@ -0,0 +1,176 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMP_H +#define PIM_IGMP_H + +#include + +#include +#include "vty.h" +#include "linklist.h" + +/* + The following sizes are likely to support + any message sent within local MTU. +*/ +#define PIM_IGMP_BUFSIZE_READ (20000) +#define PIM_IGMP_BUFSIZE_WRITE (20000) + +#define PIM_IGMP_MEMBERSHIP_QUERY (0x11) +#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12) +#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16) +#define PIM_IGMP_V2_LEAVE_GROUP (0x17) +#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22) + +#define IGMP_V3_REPORT_HEADER_SIZE (8) +#define IGMP_V3_GROUP_RECORD_MIN_SIZE (8) +#define IGMP_V3_MSG_MIN_SIZE (IGMP_V3_REPORT_HEADER_SIZE + \ + IGMP_V3_GROUP_RECORD_MIN_SIZE) +#define IGMP_V12_MSG_SIZE (8) + +#define IGMP_V3_GROUP_RECORD_TYPE_OFFSET (0) +#define IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET (1) +#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2) +#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4) +#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8) + +/* RFC 3376: 8.1. Robustness Variable - Default: 2 */ +#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) + +/* RFC 3376: 8.2. Query Interval - Default: 125 seconds */ +#define IGMP_GENERAL_QUERY_INTERVAL (125) + +/* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */ +#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100) + +/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */ +#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10) + +struct igmp_join { + struct in_addr group_addr; + struct in_addr source_addr; + int sock_fd; + time_t sock_creation; +}; + +struct igmp_sock { + int fd; + struct interface *interface; + struct in_addr ifaddr; + time_t sock_creation; + + struct thread *t_igmp_read; /* read: IGMP sockets */ + struct thread *t_igmp_query_timer; /* timer: issue IGMP general queries */ + struct thread *t_other_querier_timer; /* timer: other querier present */ + + int querier_query_interval; /* QQI */ + int querier_robustness_variable; /* QRV */ + int startup_query_count; + + struct list *igmp_group_list; /* list of struct igmp_group */ +}; + +struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, + struct in_addr ifaddr); +struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, + int fd); +struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, + struct in_addr ifaddr, + struct interface *ifp); +void igmp_sock_delete(struct igmp_sock *igmp); +void igmp_sock_free(struct igmp_sock *igmp); + +int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len); + +void pim_igmp_general_query_on(struct igmp_sock *igmp); +void pim_igmp_general_query_off(struct igmp_sock *igmp); +void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp); +void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp); + +#define IGMP_SOURCE_MASK_FORWARDING (1 << 0) +#define IGMP_SOURCE_MASK_DELETE (1 << 1) +#define IGMP_SOURCE_MASK_SEND (1 << 2) +#define IGMP_SOURCE_TEST_FORWARDING(flags) ((flags) & IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_TEST_DELETE(flags) ((flags) & IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_TEST_SEND(flags) ((flags) & IGMP_SOURCE_MASK_SEND) +#define IGMP_SOURCE_DO_FORWARDING(flags) ((flags) |= IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_DO_DELETE(flags) ((flags) |= IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_DO_SEND(flags) ((flags) |= IGMP_SOURCE_MASK_SEND) +#define IGMP_SOURCE_DONT_FORWARDING(flags) ((flags) &= ~IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_DONT_DELETE(flags) ((flags) &= ~IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_DONT_SEND(flags) ((flags) &= ~IGMP_SOURCE_MASK_SEND) + +struct igmp_source { + struct in_addr source_addr; + struct thread *t_source_timer; + struct igmp_group *source_group; /* back pointer */ + time_t source_creation; + uint32_t source_flags; + struct channel_oil *source_channel_oil; + + /* + RFC 3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + */ + int source_query_retransmit_count; +}; + +struct igmp_group { + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and it + represents the time for the *filter-mode* of the group to expire and + switch to INCLUDE mode. + */ + struct thread *t_group_timer; + + /* Shared between group-specific and + group-and-source-specific retransmissions */ + struct thread *t_group_query_retransmit_timer; + + /* Counter exclusive for group-specific retransmissions + (not used by group-and-source-specific retransmissions, + since sources have their counters) */ + int group_specific_query_retransmit_count; + + struct in_addr group_addr; + int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */ + struct list *group_source_list; /* list of struct igmp_source */ + time_t group_creation; + struct igmp_sock *group_igmp_sock; /* back pointer */ + int64_t last_igmp_v1_report_dsec; + int64_t last_igmp_v2_report_dsec; +}; + +struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr, + const char *ifname); + +void igmp_group_delete_empty_include(struct igmp_group *group); + +void igmp_startup_mode_on(struct igmp_sock *igmp); + +void igmp_group_timer_on(struct igmp_group *group, + long interval_msec, const char *ifname); + +#endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c new file mode 100644 index 00000000..693a42b7 --- /dev/null +++ b/pimd/pim_igmp_join.c @@ -0,0 +1,67 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include + +#include "pim_igmp_join.h" + +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#endif + +#ifndef MCAST_JOIN_SOURCE_GROUP +#define MCAST_JOIN_SOURCE_GROUP 46 +struct group_source_req +{ + uint32_t gsr_interface; + struct sockaddr_storage gsr_group; + struct sockaddr_storage gsr_source; +}; +#endif + +int pim_igmp_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct group_source_req req; + struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group; + struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source; + + memset(group_sa, 0, sizeof(*group_sa)); + group_sa->sin_family = AF_INET; + group_sa->sin_addr = group_addr; + group_sa->sin_port = htons(0); + + memset(source_sa, 0, sizeof(*source_sa)); + source_sa->sin_family = AF_INET; + source_sa->sin_addr = source_addr; + source_sa->sin_port = htons(0); + + req.gsr_interface = ifindex; + + return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, + &req, sizeof(req)); + + return 0; +} diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h new file mode 100644 index 00000000..1127af12 --- /dev/null +++ b/pimd/pim_igmp_join.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMP_JOIN_H +#define PIM_IGMP_JOIN_H + +#include + +int pim_igmp_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr); + +#endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c new file mode 100644 index 00000000..3baddbfa --- /dev/null +++ b/pimd/pim_igmpv3.c @@ -0,0 +1,1729 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "log.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_igmp.h" +#include "pim_igmpv3.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_time.h" +#include "pim_zebra.h" +#include "pim_oil.h" + +static void group_retransmit_timer_on(struct igmp_group *group); +static long igmp_group_timer_remain_msec(struct igmp_group *group); +static long igmp_source_timer_remain_msec(struct igmp_source *source); +static void group_query_send(struct igmp_group *group); +static void source_query_send_by_flag(struct igmp_group *group, + int num_sources_tosend); + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + if (PIM_DEBUG_IGMP_TRACE) { + char from_str[100]; + char group_str[100]; + + pim_inet4_dump("", from, from_str, sizeof(from_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + + zlog_debug("%s: from %s on %s: group=%s sources=%d", + label, from_str, ifp->name, group_str, num_sources); + } +} + +int igmp_group_compat_mode(const struct igmp_sock *igmp, + const struct igmp_group *group) +{ + struct pim_interface *pim_ifp; + int64_t now_dsec; + long older_host_present_interval_dsec; + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + /* + RFC 3376: 8.13. Older Host Present Interval + + This value MUST be ((the Robustness Variable) times (the Query + Interval)) plus (one Query Response Interval). + + older_host_present_interval_dsec = \ + igmp->querier_robustness_variable * \ + 10 * igmp->querier_query_interval + \ + pim_ifp->query_max_response_time_dsec; + */ + older_host_present_interval_dsec = + PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + now_dsec = pim_time_monotonic_dsec(); + if (now_dsec < 1) { + /* broken timer logged by pim_time_monotonic_dsec() */ + return 3; + } + + if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec) + return 1; /* IGMPv1 */ + + if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec) + return 2; /* IGMPv2 */ + + return 3; /* IGMPv3 */ +} + +void igmp_group_reset_gmi(struct igmp_group *group) +{ + long group_membership_interval_msec; + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + struct interface *ifp; + + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + + /* + RFC 3376: 8.4. Group Membership Interval + + The Group Membership Interval is the amount of time that must pass + before a multicast router decides there are no more members of a + group or a particular source on a network. + + This value MUST be ((the Robustness Variable) times (the Query + Interval)) plus (one Query Response Interval). + + group_membership_interval_msec = querier_robustness_variable * + (1000 * querier_query_interval) + + 100 * query_response_interval_dsec; + */ + group_membership_interval_msec = + PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s", + group_str, + group_membership_interval_msec / 1000, + group_membership_interval_msec % 1000, + ifp->name); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(group->group_filtermode_isexcl); + + igmp_group_timer_on(group, group_membership_interval_msec, ifp->name); +} + +static int igmp_source_timer(struct thread *t) +{ + struct igmp_source *source; + struct igmp_group *group; + + zassert(t); + source = THREAD_ARG(t); + zassert(source); + + group = source->source_group; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("%s: Source timer expired for group %s source %s on %s", + __PRETTY_FUNCTION__, + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + zassert(source->t_source_timer); + source->t_source_timer = 0; + + /* + RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + + Group + Filter-Mode Source Timer Value Action + ----------- ------------------ ------ + INCLUDE TIMER == 0 Suggest to stop forwarding + traffic from source and + remove source record. If + there are no more source + records for the group, delete + group record. + + EXCLUDE TIMER == 0 Suggest to not forward + traffic from source + (DO NOT remove record) + + Source timer switched from (T > 0) to (T == 0): disable forwarding. + */ + + zassert(!source->t_source_timer); + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + + igmp_source_forward_stop(source); + } + else { + /* INCLUDE mode */ + + /* igmp_source_delete() will stop forwarding source */ + igmp_source_delete(source); + + /* + If there are no more source records for the group, delete group + record. + */ + if (!listcount(group->group_source_list)) { + igmp_group_delete_empty_include(group); + } + } + + return 0; +} + +static void source_timer_off(struct igmp_group *group, + struct igmp_source *source) +{ + if (!source->t_source_timer) + return; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Cancelling TIMER event for group %s source %s on %s", + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + THREAD_OFF(source->t_source_timer); + zassert(!source->t_source_timer); +} + +static void igmp_source_timer_on(struct igmp_group *group, + struct igmp_source *source, + long interval_msec) +{ + source_timer_off(group, source); + + if (PIM_DEBUG_IGMP_EVENTS) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", + interval_msec / 1000, + interval_msec % 1000, + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + THREAD_TIMER_MSEC_ON(master, source->t_source_timer, + igmp_source_timer, + source, interval_msec); + zassert(source->t_source_timer); + + /* + RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + + Source timer switched from (T == 0) to (T > 0): enable forwarding. + */ + igmp_source_forward_start(source); +} + +void igmp_source_reset_gmi(struct igmp_sock *igmp, + struct igmp_group *group, + struct igmp_source *source) +{ + long group_membership_interval_msec; + struct pim_interface *pim_ifp; + struct interface *ifp; + + ifp = igmp->interface; + pim_ifp = ifp->info; + + group_membership_interval_msec = + PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + + zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s", + source_str, + group_membership_interval_msec / 1000, + group_membership_interval_msec % 1000, + group_str, + ifp->name); + } + + igmp_source_timer_on(group, source, + group_membership_interval_msec); +} + +static void source_mark_delete_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DO_DELETE(src->source_flags); + } +} + +static void source_mark_send_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DO_SEND(src->source_flags); + } +} + +static int source_mark_send_flag_by_timer(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + int num_marked_sources = 0; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + /* Is source timer running? */ + if (src->t_source_timer) { + IGMP_SOURCE_DO_SEND(src->source_flags); + ++num_marked_sources; + } + else { + IGMP_SOURCE_DONT_SEND(src->source_flags); + } + } + + return num_marked_sources; +} + +static void source_clear_send_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DONT_SEND(src->source_flags); + } +} + +/* + Any source (*,G) is forwarded only if mode is EXCLUDE {empty} +*/ +static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group) +{ + zassert(group->group_filtermode_isexcl); + + if (listcount(group->group_source_list) < 1) { + igmp_anysource_forward_start(group); + } +} + +void igmp_source_free(struct igmp_source *source) +{ + /* make sure there is no source timer running */ + zassert(!source->t_source_timer); + + XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); +} + +static void source_channel_oil_detach(struct igmp_source *source) +{ + if (source->source_channel_oil) { + pim_channel_oil_del(source->source_channel_oil); + source->source_channel_oil = 0; + } +} + +/* + igmp_source_delete: stop fowarding, and delete the source + igmp_source_forward_stop: stop fowarding, but keep the source +*/ +void igmp_source_delete(struct igmp_source *source) +{ + struct igmp_group *group; + + group = source->source_group; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s", + source_str, group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + } + + source_timer_off(group, source); + igmp_source_forward_stop(source); + + /* sanity check that forwarding has been disabled */ + if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + /* warning only */ + } + + source_channel_oil_detach(source); + + /* + notice that listnode_delete() can't be moved + into igmp_source_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(group->group_source_list, source); + + igmp_source_free(source); + + if (group->group_filtermode_isexcl) { + group_exclude_fwd_anysrc_ifempty(group); + } +} + +static void source_delete_by_flag(struct list *source_list) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) + if (IGMP_SOURCE_TEST_DELETE(src->source_flags)) + igmp_source_delete(src); +} + +void igmp_source_delete_expired(struct list *source_list) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) + if (!src->t_source_timer) + igmp_source_delete(src); +} + +struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, + struct in_addr src_addr) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) + if (src_addr.s_addr == src->source_addr.s_addr) + return src; + + return 0; +} + +static struct igmp_source *source_new(struct igmp_group *group, + struct in_addr src_addr, + const char *ifname) +{ + struct igmp_source *src; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", src_addr, source_str, sizeof(source_str)); + zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s", + source_str, group_str, + group->group_igmp_sock->fd, + ifname); + } + + src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); + if (!src) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; /* error, not found, could not create */ + } + + src->t_source_timer = 0; + src->source_group = group; /* back pointer */ + src->source_addr = src_addr; + src->source_creation = pim_time_monotonic_sec(); + src->source_flags = 0; + src->source_query_retransmit_count = 0; + src->source_channel_oil = 0; + + listnode_add(group->group_source_list, src); + + zassert(!src->t_source_timer); /* source timer == 0 */ + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + return src; +} + +static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp, + struct igmp_group *group, + struct in_addr src_addr, + const char *ifname) +{ + struct igmp_source *src; + + src = igmp_find_source_by_addr(group, src_addr); + if (src) { + return src; + } + + src = source_new(group, src_addr, ifname); + if (!src) { + return 0; + } + + return src; +} + +static void allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + int i; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + /* scan received sources */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + source = add_source_by_addr(igmp, group, *src_addr, ifp->name); + if (!source) { + continue; + } + + /* + RFC 3376: 6.4.1. Reception of Current-State Records + + When receiving IS_IN reports for groups in EXCLUDE mode is + sources should be moved from set with (timers = 0) to set with + (timers > 0). + + igmp_source_reset_gmi() below, resetting the source timers to + GMI, accomplishes this. + */ + igmp_source_reset_gmi(igmp, group, source); + + } /* scan received sources */ +} + +void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + on_trace(__PRETTY_FUNCTION__, + igmp->interface, from, group_addr, num_sources, sources); + + allow(igmp, from, group_addr, num_sources, sources); +} + +static void isex_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int i; + + /* EXCLUDE mode */ + zassert(group->group_filtermode_isexcl); + + /* E.1: set deletion flag for known sources (X,Y) */ + source_mark_delete_flag(group->group_source_list); + + /* scan received sources (A) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* E.2: lookup reported source from (A) in (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* E.4: if not found, create source with timer=GMI: (A-X-Y) */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* timer == 0 */ + igmp_source_reset_gmi(group->group_igmp_sock, group, source); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + } + + } /* scan received sources */ + + /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ + source_delete_by_flag(group->group_source_list); +} + +static void isex_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int i; + + /* INCLUDE mode */ + zassert(!group->group_filtermode_isexcl); + + /* I.1: set deletion flag for known sources (A) */ + source_mark_delete_flag(group->group_source_list); + + /* scan received sources (B) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* I.2: lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* I.3: if found, clear deletion flag (A*B) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* I.4: if not found, create source with timer=0 (B-A) */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* (B-A) timer=0 */ + } + + } /* scan received sources */ + + /* I.5: delete all sources marked with deletion flag (A-B) */ + source_delete_by_flag(group->group_source_list); + + group->group_filtermode_isexcl = 1; /* boolean=true */ + + zassert(group->group_filtermode_isexcl); + + group_exclude_fwd_anysrc_ifempty(group); +} + +void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + isex_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + isex_incl(group, num_sources, sources); + zassert(group->group_filtermode_isexcl); + } + + zassert(group->group_filtermode_isexcl); + + igmp_group_reset_gmi(group); +} + +static void toin_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + struct igmp_sock *igmp = group->group_igmp_sock; + int num_sources_tosend = listcount(group->group_source_list); + int i; + + /* Set SEND flag for all known sources (A) */ + source_mark_send_flag(group->group_source_list); + + /* Scan received sources (B) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* If found, clear SEND flag (A*B) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; + } + else { + /* If not found, create new source */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + } + + /* (B)=GMI */ + igmp_source_reset_gmi(igmp, group, source); + } + + /* Send sources marked with SEND flag: Q(G,A-B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +static void toin_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + struct igmp_sock *igmp = group->group_igmp_sock; + int num_sources_tosend; + int i; + + /* Set SEND flag for X (sources with timer > 0) */ + num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list); + + /* Scan received sources (A) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (A) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + if (source->t_source_timer) { + /* If found and timer running, clear SEND flag (X*A) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; + } + } + else { + /* If not found, create new source */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + } + + /* (A)=GMI */ + igmp_source_reset_gmi(igmp, group, source); + } + + /* Send sources marked with SEND flag: Q(G,X-A) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } + + /* Send Q(G) */ + group_query_send(group); +} + +void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + toin_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + toin_incl(group, num_sources, sources); + } +} + +static void toex_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + int i; + + zassert(!group->group_filtermode_isexcl); + + /* Set DELETE flag for all known sources (A) */ + source_mark_delete_flag(group->group_source_list); + + /* Clear off SEND flag from all known sources (A) */ + source_clear_send_flag(group->group_source_list); + + /* Scan received sources (B) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* If found, clear deletion flag: (A*B) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + /* and set SEND flag (A*B) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + else { + /* If source not found, create source with timer=0: (B-A)=0 */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* (B-A) timer=0 */ + } + + } /* Scan received sources (B) */ + + group->group_filtermode_isexcl = 1; /* boolean=true */ + + /* Delete all sources marked with DELETE flag (A-B) */ + source_delete_by_flag(group->group_source_list); + + /* Send sources marked with SEND flag: Q(G,A*B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } + + zassert(group->group_filtermode_isexcl); + + group_exclude_fwd_anysrc_ifempty(group); +} + +static void toex_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + int i; + + /* set DELETE flag for all known sources (X,Y) */ + source_mark_delete_flag(group->group_source_list); + + /* clear off SEND flag from all known sources (X,Y) */ + source_clear_send_flag(group->group_source_list); + + /* scan received sources (A) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* if found, clear off DELETE flag from reported source (A) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */ + long group_timer_msec; + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + + zassert(!source->t_source_timer); /* timer == 0 */ + group_timer_msec = igmp_group_timer_remain_msec(group); + igmp_source_timer_on(group, source, group_timer_msec); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + + /* make sure source is created with DELETE flag unset */ + zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); + } + + /* make sure reported source has DELETE flag unset */ + zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); + + if (source->t_source_timer) { + /* if source timer>0 mark SEND flag: Q(G,A-Y) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + + } /* scan received sources (A) */ + + /* + delete all sources marked with DELETE flag: + Delete (X-A) + Delete (Y-A) + */ + source_delete_by_flag(group->group_source_list); + + /* send sources marked with SEND flag: Q(G,A-Y) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + toex_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + toex_incl(group, num_sources, sources); + zassert(group->group_filtermode_isexcl); + } + zassert(group->group_filtermode_isexcl); + + /* Group Timer=GMI */ + igmp_group_reset_gmi(group); +} + +void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + on_trace(__PRETTY_FUNCTION__, + igmp->interface, from, group_addr, num_sources, sources); + + allow(igmp, from, group_addr, num_sources, sources); +} + +/* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries + + When transmitting a group specific query, if the group timer is + larger than LMQT, the "Suppress Router-Side Processing" bit is set + in the query message. +*/ +static void group_retransmit_group(struct igmp_group *group) +{ + char query_buf[PIM_IGMP_BUFSIZE_WRITE]; + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + int s_flag; + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries + + When transmitting a group specific query, if the group timer is + larger than LMQT, the "Suppress Router-Side Processing" bit is set + in the query message. + */ + s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", + group_str, igmp->interface->name, s_flag, + group->group_specific_query_retransmit_count); + } + + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources_tosend */, + group->group_addr /* dst_addr */, + group->group_addr /* group_addr */, + pim_ifp->igmp_specific_query_max_response_time_dsec, + s_flag, + igmp->querier_robustness_variable, + igmp->querier_query_interval); +} + +/* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + + When building a group and source specific query for a group G, two + separate query messages are sent for the group. The first one has + the "Suppress Router-Side Processing" bit set and contains all the + sources with retransmission state and timers greater than LMQT. The + second has the "Suppress Router-Side Processing" bit clear and + contains all the sources with retransmission state and timers lower + or equal to LMQT. If either of the two calculated messages does not + contain any sources, then its transmission is suppressed. + */ +static int group_retransmit_sources(struct igmp_group *group, + int send_with_sflag_set) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */ + char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */ + int query_buf1_max_sources; + int query_buf2_max_sources; + struct in_addr *source_addr1; + struct in_addr *source_addr2; + int num_sources_tosend1; + int num_sources_tosend2; + struct listnode *src_node; + struct igmp_source *src; + int num_retransmit_sources_left = 0; + + query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; + query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; + + source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); + source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* Scan all group sources */ + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { + + /* Source has retransmission state? */ + if (src->source_query_retransmit_count < 1) + continue; + + if (--src->source_query_retransmit_count > 0) { + ++num_retransmit_sources_left; + } + + /* Copy source address into appropriate query buffer */ + if (igmp_source_timer_remain_msec(src) > lmqt_msec) { + *source_addr1 = src->source_addr; + ++source_addr1; + } + else { + *source_addr2 = src->source_addr; + ++source_addr2; + } + + } + + num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); + num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", + group_str, igmp->interface->name, + num_sources_tosend1, + num_sources_tosend2, + send_with_sflag_set, + num_retransmit_sources_left); + } + + if (num_sources_tosend1 > 0) { + /* + Send group-and-source-specific query with s_flag set and all + sources with timers greater than LMQT. + */ + + if (send_with_sflag_set) { + + query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; + if (num_sources_tosend1 > query_buf1_max_sources) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", + __PRETTY_FUNCTION__, group_str, igmp->interface->name, + num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); + } + else { + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf1, + sizeof(query_buf1), + num_sources_tosend1, + group->group_addr, + group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + 1 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + } + + } /* send_with_sflag_set */ + + } + + if (num_sources_tosend2 > 0) { + /* + Send group-and-source-specific query with s_flag clear and all + sources with timers lower or equal to LMQT. + */ + + query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; + if (num_sources_tosend2 > query_buf2_max_sources) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", + __PRETTY_FUNCTION__, group_str, igmp->interface->name, + num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); + } + else { + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf2, + sizeof(query_buf2), + num_sources_tosend2, + group->group_addr, + group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + 0 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + } + } + + return num_retransmit_sources_left; +} + +static int igmp_group_retransmit(struct thread *t) +{ + struct igmp_group *group; + int num_retransmit_sources_left; + int send_with_sflag_set; /* boolean */ + + zassert(t); + group = THREAD_ARG(t); + zassert(group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("group_retransmit_timer: group %s on %s", + group_str, group->group_igmp_sock->interface->name); + } + + /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ + if (group->group_specific_query_retransmit_count > 0) { + + /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */ + group_retransmit_group(group); + --group->group_specific_query_retransmit_count; + + /* + RFC3376: 6.6.3.2 + If a group specific query is scheduled to be transmitted at the + same time as a group and source specific query for the same group, + then transmission of the group and source specific message with the + "Suppress Router-Side Processing" bit set may be suppressed. + */ + send_with_sflag_set = 0; /* boolean=false */ + } + else { + send_with_sflag_set = 1; /* boolean=true */ + } + + /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */ + num_retransmit_sources_left = group_retransmit_sources(group, + send_with_sflag_set); + + group->t_group_query_retransmit_timer = 0; + + /* + Keep group retransmit timer running if there is any retransmit + counter pending + */ + if ((num_retransmit_sources_left > 0) || + (group->group_specific_query_retransmit_count > 0)) { + group_retransmit_timer_on(group); + } + + return 0; +} + +/* + group_retransmit_timer_on: + if group retransmit timer isn't running, starts it; + otherwise, do nothing +*/ +static void group_retransmit_timer_on(struct igmp_group *group) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqi_msec; /* Last Member Query Interval */ + + /* if group retransmit timer is running, do nothing */ + if (group->t_group_query_retransmit_timer) { + return; + } + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s", + lmqi_msec / 1000, + lmqi_msec % 1000, + group_str, + igmp->interface->name); + } + + THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer, + igmp_group_retransmit, + group, lmqi_msec); +} + +static long igmp_group_timer_remain_msec(struct igmp_group *group) +{ + return pim_time_timer_remain_msec(group->t_group_timer); +} + +static long igmp_source_timer_remain_msec(struct igmp_source *source) +{ + return pim_time_timer_remain_msec(source->t_source_timer); +} + +/* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries +*/ +static void group_query_send(struct igmp_group *group) +{ + long lmqc; /* Last Member Query Count */ + + lmqc = group->group_igmp_sock->querier_robustness_variable; + + /* lower group timer to lmqt */ + igmp_group_timer_lower_to_lmqt(group); + + /* reset retransmission counter */ + group->group_specific_query_retransmit_count = lmqc; + + /* immediately send group specific query (decrease retransmit counter by 1)*/ + group_retransmit_group(group); + + /* make sure group retransmit timer is running */ + group_retransmit_timer_on(group); +} + +/* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries +*/ +static void source_query_send_by_flag(struct igmp_group *group, + int num_sources_tosend) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + struct listnode *src_node; + struct igmp_source *src; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + + zassert(num_sources_tosend > 0); + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + + (...) for each of the sources in X of group G, with source timer larger + than LMQT: + o Set number of retransmissions for each source to [Last Member + Query Count]. + o Lower source timer to LMQT. + */ + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { + if (IGMP_SOURCE_TEST_SEND(src->source_flags)) { + /* source "src" in X of group G */ + if (igmp_source_timer_remain_msec(src) > lmqt_msec) { + src->source_query_retransmit_count = lmqc; + igmp_source_timer_lower_to_lmqt(src); + } + } + } + + /* send group-and-source specific queries */ + group_retransmit_sources(group, 1 /* send_with_sflag_set=true */); + + /* make sure group retransmit timer is running */ + group_retransmit_timer_on(group); +} + +static void block_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + int i; + + /* 1. clear off SEND flag from all known sources (X,Y) */ + source_clear_send_flag(group->group_source_list); + + /* 2. scan received sources (A) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (!source) { + /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */ + long group_timer_msec; + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + + zassert(!source->t_source_timer); /* timer == 0 */ + group_timer_msec = igmp_group_timer_remain_msec(group); + igmp_source_timer_on(group, source, group_timer_msec); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + } + + if (source->t_source_timer) { + /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + } + + /* 5. send sources marked with SEND flag: Q(G,A-Y) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +static void block_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + int i; + + /* 1. clear off SEND flag from all known sources (B) */ + source_clear_send_flag(group->group_source_list); + + /* 2. scan received sources (A) */ + for (i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + } + + /* 4. send sources marked with SEND flag: Q(G,A*B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + block_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + block_incl(group, num_sources, sources); + } +} + +void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) +{ + struct igmp_sock *igmp; + struct interface *ifp; + struct pim_interface *pim_ifp; + char *ifname; + int lmqi_dsec; /* Last Member Query Interval */ + int lmqc; /* Last Member Query Count */ + int lmqt_msec; /* Last Member Query Time */ + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + if (!group->group_filtermode_isexcl) { + return; + } + + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + ifname = ifp->name; + + lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; + lmqc = igmp->querier_robustness_variable; + lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", + __PRETTY_FUNCTION__, + group_str, ifname, + lmqc, lmqi_dsec, lmqt_msec); + } + + zassert(group->group_filtermode_isexcl); + + igmp_group_timer_on(group, lmqt_msec, ifname); +} + +void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) +{ + struct igmp_group *group; + struct igmp_sock *igmp; + struct interface *ifp; + struct pim_interface *pim_ifp; + char *ifname; + int lmqi_dsec; /* Last Member Query Interval */ + int lmqc; /* Last Member Query Count */ + int lmqt_msec; /* Last Member Query Time */ + + group = source->source_group; + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + ifname = ifp->name; + + lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; + lmqc = igmp->querier_robustness_variable; + lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", + __PRETTY_FUNCTION__, + group_str, source_str, ifname, + lmqc, lmqi_dsec, lmqt_msec); + } + + igmp_source_timer_on(group, source, lmqt_msec); +} + +/* + Copy sources to message: + + struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET); + if (num_sources > 0) { + struct listnode *node; + struct igmp_source *src; + int i = 0; + + for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) { + sources[i++] = src->source_addr; + } + } +*/ +void pim_igmp_send_membership_query(struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval) +{ + ssize_t msg_size; + uint8_t max_resp_code; + uint8_t qqic; + ssize_t sent; + struct sockaddr_in to; + socklen_t tolen; + uint16_t checksum; + + zassert(num_sources >= 0); + + msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); + if (msg_size > query_buf_size) { + zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", + __FILE__, __PRETTY_FUNCTION__, + msg_size, query_buf_size); + return; + } + + s_flag = PIM_FORCE_BOOLEAN(s_flag); + zassert((s_flag == 0) || (s_flag == 1)); + + max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); + qqic = igmp_msg_encode16to8(querier_query_interval); + + /* + RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + + If non-zero, the QRV field contains the [Robustness Variable] + value used by the querier, i.e., the sender of the Query. If the + querier's [Robustness Variable] exceeds 7, the maximum value of + the QRV field, the QRV is set to zero. + */ + if (querier_robustness_variable > 7) { + querier_robustness_variable = 0; + } + + query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; + query_buf[1] = max_resp_code; + *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + memcpy(query_buf+4, &group_addr, sizeof(struct in_addr)); + + query_buf[8] = (s_flag << 3) | querier_robustness_variable; + query_buf[9] = qqic; + *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); + + checksum = in_cksum(query_buf, msg_size); + *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum; + + if (PIM_DEBUG_IGMP_PACKETS) { + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, num_sources, + msg_size, s_flag, querier_robustness_variable, + querier_query_interval, qqic, checksum); + } + +#if 0 + memset(&to, 0, sizeof(to)); +#endif + to.sin_family = AF_INET; + to.sin_addr = dst_addr; +#if 0 + to.sin_port = htons(0); +#endif + tolen = sizeof(to); + + sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen); + if (sent != (ssize_t) msg_size) { + int e = errno; + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, msg_size, + e, safe_strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, + msg_size, sent); + } + return; + } + + /* + s_flag sanity test: s_flag must be set for general queries + + RFC 3376: 6.6.1. Timer Updates + + When a router sends or receives a query with a clear Suppress + Router-Side Processing flag, it must update its timers to reflect + the correct timeout values for the group or sources being queried. + + General queries don't trigger timer update. + */ + if (!s_flag) { + /* general query? */ + if (PIM_INADDR_IS_ANY(group_addr)) { + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, num_sources); + } + } + +} diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h new file mode 100644 index 00000000..bb7e9267 --- /dev/null +++ b/pimd/pim_igmpv3.h @@ -0,0 +1,100 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMPV3_H +#define PIM_IGMPV3_H + +#include +#include "if.h" + +#define IGMP_V3_CHECKSUM_OFFSET (2) +#define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6) +#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8) +#define IGMP_V3_NUMSOURCES_OFFSET (10) +#define IGMP_V3_SOURCES_OFFSET (12) + +/* GMI: Group Membership Interval */ +#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec)) + +/* OQPI: Other Querier Present Interval */ +#define PIM_IGMP_OQPI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * ((qri_dsec) >> 1)) + +/* SQI: Startup Query Interval */ +#define PIM_IGMP_SQI(qi) (((qi) < 4) ? 1 : ((qi) >> 2)) + +/* LMQT: Last Member Query Time */ +#define PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc) ((lmqc) * (100 * (lmqi_dsec))) + +/* OHPI: Older Host Present Interval */ +#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec)) + +void igmp_group_reset_gmi(struct igmp_group *group); +void igmp_source_reset_gmi(struct igmp_sock *igmp, + struct igmp_group *group, + struct igmp_source *source); + +void igmp_source_free(struct igmp_source *source); +void igmp_source_delete(struct igmp_source *source); +void igmp_source_delete_expired(struct list *source_list); + +int igmp_group_compat_mode(const struct igmp_sock *igmp, + const struct igmp_group *group); + +void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); + +void igmp_group_timer_lower_to_lmqt(struct igmp_group *group); +void igmp_source_timer_lower_to_lmqt(struct igmp_source *source); + +struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, + struct in_addr src_addr); + +void pim_igmp_send_membership_query(struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval); + +#endif /* PIM_IGMPV3_H */ diff --git a/pimd/pim_int.c b/pimd/pim_int.c new file mode 100644 index 00000000..2ff1a116 --- /dev/null +++ b/pimd/pim_int.c @@ -0,0 +1,44 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include + +#include "pim_int.h" + +uint32_t pim_read_uint32_host(const uint8_t *buf) +{ + uint32_t val; + memcpy(&val, buf, sizeof(val)); + /* val is in netorder */ + val = ntohl(val); + /* val is in hostorder */ + return val; +} + +void pim_write_uint32(uint8_t *buf, uint32_t val_host) +{ + /* val_host is in host order */ + val_host = htonl(val_host); + /* val_host is in netorder */ + memcpy(buf, &val_host, sizeof(val_host)); +} diff --git a/pimd/pim_int.h b/pimd/pim_int.h new file mode 100644 index 00000000..d64b1032 --- /dev/null +++ b/pimd/pim_int.h @@ -0,0 +1,31 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_INT_H +#define PIM_INT_H + +#include + +uint32_t pim_read_uint32_host(const uint8_t *buf); +void pim_write_uint32(uint8_t *buf, uint32_t val_host); + +#endif /* PIM_INT_H */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c new file mode 100644 index 00000000..9d8e0012 --- /dev/null +++ b/pimd/pim_join.c @@ -0,0 +1,445 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_join.h" +#include "pim_iface.h" +#include "pim_hello.h" +#include "pim_ifchannel.h" + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static void recv_join(struct interface *ifp, + struct pim_neighbor *neigh, + uint16_t holdtime, + struct in_addr upstream, + struct in_addr group, + struct in_addr source, + uint8_t source_flags) +{ + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + source_flags & PIM_RPT_BIT_MASK, + source_flags & PIM_WILDCARD_BIT_MASK, + up_str, holdtime, neigh_str, ifp->name); + } + + /* Restart join expiry timer */ + pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, + source, group, source_flags, holdtime); +} + +static void recv_prune(struct interface *ifp, + struct pim_neighbor *neigh, + uint16_t holdtime, + struct in_addr upstream, + struct in_addr group, + struct in_addr source, + uint8_t source_flags) +{ + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + source_flags & PIM_RPT_BIT_MASK, + source_flags & PIM_WILDCARD_BIT_MASK, + up_str, holdtime, neigh_str, ifp->name); + } + + pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime); +} + +int pim_joinprune_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + uint8_t *tlv_buf, int tlv_buf_size) +{ + struct prefix msg_upstream_addr; + uint8_t msg_num_groups; + uint16_t msg_holdtime; + int addr_offset; + uint8_t *buf; + uint8_t *pastend; + int remain; + int group; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + buf = tlv_buf; + pastend = tlv_buf + tlv_buf_size; + + /* + Parse ucast addr + */ + addr_offset = pim_parse_addr_ucast(ifp->name, src_addr, + &msg_upstream_addr, + buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif + if (addr_offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -1; + } + buf += addr_offset; + + /* + Check upstream address family + */ + if (msg_upstream_addr.family != AF_INET) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s", + __PRETTY_FUNCTION__, + msg_upstream_addr.family, src_str, ifp->name); + } + return -2; + } + + remain = pastend - buf; + if (remain < 4) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, 4, src_str, ifp->name); + return -4; + } + + ++buf; /* skip reserved byte */ + msg_num_groups = *(const uint8_t *) buf; + ++buf; + msg_holdtime = ntohs(*(const uint16_t *) buf); + ++buf; + ++buf; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char upstream_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", msg_upstream_addr.u.prefix4, + upstream_str, sizeof(upstream_str)); + zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + upstream_str, msg_num_groups, msg_holdtime, + src_str, ifp->name); + } + + /* Scan groups */ + for (group = 0; group < msg_num_groups; ++group) { + struct prefix msg_group_addr; + struct prefix msg_source_addr; + uint8_t msg_source_flags; + uint16_t msg_num_joined_sources; + uint16_t msg_num_pruned_sources; + int source; + + addr_offset = pim_parse_addr_group(ifp->name, src_addr, + &msg_group_addr, + buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_group addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif + if (addr_offset < 1) { + return -5; + } + buf += addr_offset; + + remain = pastend - buf; + if (remain < 4) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, 4, src_str, ifp->name); + return -6; + } + + msg_num_joined_sources = ntohs(*(const uint16_t *) buf); + buf += 2; + msg_num_pruned_sources = ntohs(*(const uint16_t *) buf); + buf += 2; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char upstream_str[100]; + char group_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", msg_upstream_addr.u.prefix4, + upstream_str, sizeof(upstream_str)); + pim_inet4_dump("", msg_group_addr.u.prefix4, + group_str, sizeof(group_str)); + zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s", + __PRETTY_FUNCTION__, + upstream_str, group_str, msg_group_addr.prefixlen, + msg_num_joined_sources, msg_num_pruned_sources, + src_str, ifp->name); + } + + /* Scan joined sources */ + for (source = 0; source < msg_num_joined_sources; ++source) { + addr_offset = pim_parse_addr_source(ifp->name, src_addr, + &msg_source_addr, + &msg_source_flags, + buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_source addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif + if (addr_offset < 1) { + return -7; + } + + buf += addr_offset; + + recv_join(ifp, neigh, msg_holdtime, + msg_upstream_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_source_addr.u.prefix4, + msg_source_flags); + } + + /* Scan pruned sources */ + for (source = 0; source < msg_num_pruned_sources; ++source) { + addr_offset = pim_parse_addr_source(ifp->name, src_addr, + &msg_source_addr, + &msg_source_flags, + buf, pastend - buf); + if (addr_offset < 1) { + return -8; + } + + buf += addr_offset; + + recv_prune(ifp, neigh, msg_holdtime, + msg_upstream_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_source_addr.u.prefix4, + msg_source_flags); + } + + } /* scan groups */ + + return 0; +} + +int pim_joinprune_send(struct interface *ifp, + struct in_addr upstream_addr, + struct in_addr source_addr, + struct in_addr group_addr, + int send_join) +{ + struct pim_interface *pim_ifp; + uint8_t pim_msg[1000]; + const uint8_t *pastend = pim_msg + sizeof(pim_msg); + uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */ + int pim_msg_size; + int remain; + + zassert(ifp); + + pim_ifp = ifp->info; + + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + char dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s", + __PRETTY_FUNCTION__, + send_join ? "Join" : "Prune", + source_str, group_str, dst_str, ifp->name); + } + + if (PIM_INADDR_IS_ANY(upstream_addr)) { + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + char dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s", + __PRETTY_FUNCTION__, + send_join ? "Join" : "Prune", + source_str, group_str, dst_str, ifp->name); + } + return 0; + } + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ + pim_hello_require(ifp); + + /* + Build PIM message + */ + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + upstream_addr); + if (!pim_msg_curr) { + char dst_str[100]; + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_warn("%s: failure encoding destination address %s: space left=%d", + __PRETTY_FUNCTION__, dst_str, remain); + return -3; + } + + remain = pastend - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: group will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -4; + } + + *pim_msg_curr = 0; /* reserved */ + ++pim_msg_curr; + *pim_msg_curr = 1; /* number of groups */ + ++pim_msg_curr; + *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: failure encoding group address %s: space left=%d", + __PRETTY_FUNCTION__, group_str, remain); + return -5; + } + + remain = pastend - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: sources will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -6; + } + + /* number of joined sources */ + *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0); + ++pim_msg_curr; + ++pim_msg_curr; + + /* number of pruned sources */ + *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure encoding source address %s: space left=%d", + __PRETTY_FUNCTION__, source_str, remain); + return -7; + } + + /* Add PIM header */ + + pim_msg_size = pim_msg_curr - pim_msg; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_JOIN_PRUNE); + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -8; + } + + return 0; +} diff --git a/pimd/pim_join.h b/pimd/pim_join.h new file mode 100644 index 00000000..37ec0f45 --- /dev/null +++ b/pimd/pim_join.h @@ -0,0 +1,43 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_JOIN_H +#define PIM_JOIN_H + +#include + +#include "if.h" + +#include "pim_neighbor.h" + +int pim_joinprune_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + uint8_t *tlv_buf, int tlv_buf_size); + +int pim_joinprune_send(struct interface *ifp, + struct in_addr upstream_addr, + struct in_addr source_addr, + struct in_addr group_addr, + int send_join); + +#endif /* PIM_JOIN_H */ diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c new file mode 100644 index 00000000..3f565325 --- /dev/null +++ b/pimd/pim_macro.c @@ -0,0 +1,437 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" + +#include "pim_macro.h" +#include "pimd.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_ifchannel.h" + +#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr) + +/* + DownstreamJPState(S,G,I) is the per-interface state machine for + receiving (S,G) Join/Prune messages. + + DownstreamJPState(S,G,I) is either Join or Prune-Pending ? +*/ +static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch) +{ + return ch->ifjoin_state != PIM_IFJOIN_NOINFO; +} + +/* + The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD + module or other local membership mechanism has determined that local + members on interface I desire to receive traffic sent specifically + by S to G. +*/ +static int local_receiver_include(const struct pim_ifchannel *ch) +{ + /* local_receiver_include(S,G,I) ? */ + return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE; +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + The set "joins(S,G)" is the set of all interfaces on which the + router has received (S,G) Joins: + + joins(S,G) = + { all interfaces I such that + DownstreamJPState(S,G,I) is either Join or Prune-Pending } + + DownstreamJPState(S,G,I) is either Join or Prune-Pending ? +*/ +int pim_macro_chisin_joins(const struct pim_ifchannel *ch) +{ + return downstream_jpstate_isjoined(ch); +} + +/* + RFC 4601: 4.6.5. Assert State Macros + + The set "lost_assert(S,G)" is the set of all interfaces on which the + router has received (S,G) joins but has lost an (S,G) assert. + + lost_assert(S,G) = + { all interfaces I such that + lost_assert(S,G,I) == TRUE } + + bool lost_assert(S,G,I) { + if ( RPF_interface(S) == I ) { + return FALSE + } else { + return ( AssertWinner(S,G,I) != NULL AND + AssertWinner(S,G,I) != me AND + (AssertWinnerMetric(S,G,I) is better + than spt_assert_metric(S,I) ) + } + } + + AssertWinner(S,G,I) is the IP source address of the Assert(S,G) + packet that won an Assert. +*/ +int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_assert_metric spt_assert_metric; + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + /* RPF_interface(S) == I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) + return 0; /* false */ + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return 0; /* false */ + } + + if (PIM_INADDR_IS_ANY(ch->ifassert_winner)) + return 0; /* false */ + + /* AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 0; /* false */ + + spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf, + pim_ifp->primary_address); + + return pim_assert_metric_better(&ch->ifassert_winner_metric, + &spt_assert_metric); +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + pim_include(S,G) = + { all interfaces I such that: + ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE ) + OR AssertWinner(S,G,I) == me ) + AND local_receiver_include(S,G,I) } + + AssertWinner(S,G,I) is the IP source address of the Assert(S,G) + packet that won an Assert. +*/ +int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp = ch->interface->info; + + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return 0; /* false */ + } + + /* local_receiver_include(S,G,I) ? */ + if (!local_receiver_include(ch)) + return 0; /* false */ + + /* OR AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 1; /* true */ + + return ( + /* I_am_DR( I ) ? */ + PIM_IFP_I_am_DR(pim_ifp) + && + /* lost_assert(S,G,I) == FALSE ? */ + (!pim_macro_ch_lost_assert(ch)) + ); +} + +int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch) +{ + if (pim_macro_chisin_joins(ch)) + return 1; /* true */ + + return pim_macro_chisin_pim_include(ch); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + CouldAssert(S,G,I) = + SPTbit(S,G)==TRUE + AND (RPF_interface(S) != I) + AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) + (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) + (-) lost_assert(*,G) + (+) joins(S,G) (+) pim_include(S,G) ) ) + + CouldAssert(S,G,I) is true for downstream interfaces that would be in + the inherited_olist(S,G) if (S,G) assert information was not taken + into account. + + CouldAssert(S,G,I) may be affected by changes in the following: + + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface +*/ +int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch) +{ + struct interface *ifp; + + /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */ + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + /* RPF_interface(S) != I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) + return 0; /* false */ + + /* I in joins(S,G) (+) pim_include(S,G) ? */ + return pim_macro_chisin_joins_or_include(ch); +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + spt_assert_metric(S,I) gives the assert metric we use if we're + sending an assert based on active (S,G) forwarding state: + + assert_metric + spt_assert_metric(S,I) { + return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)} + } +*/ +struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf, + struct in_addr ifaddr) +{ + struct pim_assert_metric metric; + + metric.rpt_bit_flag = 0; + metric.metric_preference = rpf->source_nexthop.mrib_metric_preference; + metric.route_metric = rpf->source_nexthop.mrib_route_metric; + metric.ip_address = ifaddr; + + return metric; +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + An assert metric for (S,G) to include in (or compare against) an + Assert message sent on interface I should be computed using the + following pseudocode: + + assert_metric my_assert_metric(S,G,I) { + if( CouldAssert(S,G,I) == TRUE ) { + return spt_assert_metric(S,I) + } else if( CouldAssert(*,G,I) == TRUE ) { + return rpt_assert_metric(G,I) + } else { + return infinite_assert_metric() + } + } +*/ +struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ch->interface->info; + + if (pim_ifp) { + if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); + } + } + + return qpim_infinite_assert_metric; +} + +/* + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) +*/ +static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch) +{ + if (pim_macro_ch_lost_assert(ch)) + return 0; /* false */ + + return pim_macro_chisin_joins_or_include(ch); +} + +/* + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Additionally, the Packet forwarding rules of Section 4.2 can be + simplified in a PIM-SSM-only router: + + iif is the incoming interface of the packet. + oiflist = NULL + if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) { + oiflist = inherited_olist(S,G) + } else if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + oiflist = oiflist (-) iif + forward packet on all interfaces in oiflist + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + + Note: + - The following test is performed as response to WRONGVIF kernel + upcall: + if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + See pim_mroute.c mroute_msg(). +*/ +int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch) +{ + if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) { + /* oiflist is NULL */ + return 0; /* false */ + } + + /* oiflist = oiflist (-) iif */ + if (ch->interface == ch->upstream->rpf.source_nexthop.interface) + return 0; /* false */ + + return pim_macro_chisin_inherited_olist(ch); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + AssertTrackingDesired(S,G,I) = + (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) + (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) + (-) lost_assert(*,G) + (+) joins(S,G) ) ) + OR (local_receiver_include(S,G,I) == TRUE + AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me))) + OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE)) + OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE) + AND (SPTbit(S,G) == FALSE)) + + AssertTrackingDesired(S,G,I) is true on any interface in which an + (S,G) assert might affect our behavior. +*/ +int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return 0; /* false */ + } + + /* I in joins(S,G) ? */ + if (pim_macro_chisin_joins(ch)) + return 1; /* true */ + + /* local_receiver_include(S,G,I) ? */ + if (local_receiver_include(ch)) { + /* I_am_DR(I) ? */ + if (PIM_IFP_I_am_DR(pim_ifp)) + return 1; /* true */ + + /* AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 1; /* true */ + } + + /* RPF_interface(S) == I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) { + /* JoinDesired(S,G) ? */ + if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)) + return 1; /* true */ + } + + return 0; /* false */ +} + diff --git a/pimd/pim_macro.h b/pimd/pim_macro.h new file mode 100644 index 00000000..472fa9b4 --- /dev/null +++ b/pimd/pim_macro.h @@ -0,0 +1,44 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MACRO_H +#define PIM_MACRO_H + +#include + +#include "if.h" + +#include "pim_upstream.h" +#include "pim_ifchannel.h" + +int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch); +int pim_macro_chisin_joins(const struct pim_ifchannel *ch); +int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch); +int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch); +int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch); +struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf, + struct in_addr ifaddr); +struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch); +int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch); +int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch); + +#endif /* PIM_MACRO_H */ diff --git a/pimd/pim_main.c b/pimd/pim_main.c new file mode 100644 index 00000000..b57f8811 --- /dev/null +++ b/pimd/pim_main.c @@ -0,0 +1,294 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "privs.h" +#include "version.h" +#include +#include "command.h" +#include "thread.h" +#include + +#include "memory.h" +#include "filter.h" +#include "vty.h" +#include "sigevent.h" +#include "version.h" + +#include "pimd.h" +#include "pim_version.h" +#include "pim_signals.h" +#include "pim_zebra.h" + +#ifdef PIM_ZCLIENT_DEBUG +extern int zclient_debug; +#endif + +extern struct host host; + +char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG; + +struct option longopts[] = { + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "pid_file", required_argument, NULL, 'i'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "version", no_argument, NULL, 'v'}, + { "debug_zclient", no_argument, NULL, 'Z'}, + { "help", no_argument, NULL, 'h'}, + { 0 } +}; + +/* pimd privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, +}; + +/* pimd privileges to run with */ +struct zebra_privs_t pimd_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]), + .cap_num_i = 0 +}; + +char* progname; +const char *pid_file = PATH_PIMD_PID; + +static void usage(int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else { + printf ("Usage : %s [OPTION...]\n\ +Daemon which manages PIM.\n\n\ +-d, --daemon Run in daemon mode\n\ +-f, --config_file Set configuration file name\n\ +-i, --pid_file Set process identifier file name\n\ +-z, --socket Set path of zebra socket\n\ +-A, --vty_addr Set vty's bind address\n\ +-P, --vty_port Set vty's port number\n\ +-v, --version Print program version\n\ +" + +#ifdef PIM_ZCLIENT_DEBUG +"\ +-Z, --debug_zclient Enable zclient debugging\n\ +" +#endif + +"\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, PIMD_BUG_ADDRESS); + } + + exit (status); +} + + +int main(int argc, char** argv, char** envp) { + char *p; + char *vty_addr = NULL; + int vty_port = -1; + int daemon_mode = 0; + char *config_file = NULL; + char *zebra_sock_path = NULL; + struct thread thread; + + umask(0027); + + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog(progname, ZLOG_PIM, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* this while just reads the options */ + while (1) { + int opt; + + opt = getopt_long (argc, argv, "df:i:z:A:P:vZh", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zebra_sock_path = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + vty_port = atoi (optarg); + break; + case 'v': + printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION); + print_version(QUAGGA_PROGNAME); + exit (0); + break; +#ifdef PIM_ZCLIENT_DEBUG + case 'Z': + zclient_debug = 1; + break; +#endif + case 'h': + usage (0); + break; + default: + usage (1); + break; + } + } + + master = thread_master_create(); + + /* + * Temporarily send zlog to stdout + */ + zlog_default->maxlvl[ZLOG_DEST_STDOUT] = zlog_default->default_lvl; + zlog_notice("Boot logging temporarily directed to stdout - begin"); + + zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting", + QUAGGA_VERSION, PIMD_VERSION); + + /* + * Initializations + */ + zprivs_init (&pimd_privs); + pim_signals_init(); + cmd_init(1); + vty_init(master); + memory_init(); + access_list_init(); + pim_init(); + + /* + * reset zlog default, then will obey configuration file + */ + zlog_notice("Boot logging temporarily directed to stdout - end"); +#if 0 + /* this would disable logging to stdout, but config has not been + loaded yet to reconfig the logging output */ + zlog_default->maxlvl[ZLOG_DEST_STDOUT] = ZLOG_DISABLED; +#endif + + /* + Initialize zclient "update" and "lookup" sockets + */ + pim_zebra_init(zebra_sock_path); + + zlog_notice("Loading configuration - begin"); + + /* Get configuration file. */ + vty_read_config(config_file, config_default); + + /* + Starting from here zlog_* functions will log according configuration + */ + + zlog_notice("Loading configuration - end"); + + /* Change to the daemon program. */ + if (daemon_mode) { + if (daemon(0, 0)) { + zlog_warn("failed to daemonize"); + } + } + + /* Process ID file creation. */ + pid_output(pid_file); + + /* Create pimd VTY socket */ + if (vty_port < 0) + vty_port = PIMD_VTY_PORT; + vty_serv_sock(vty_addr, vty_port, PIM_VTYSH_PATH); + + zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d", + QUAGGA_VERSION, PIMD_VERSION, vty_port); + +#ifdef PIM_DEBUG_BYDEFAULT + zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands"); + PIM_DO_DEBUG_PIM_EVENTS; + PIM_DO_DEBUG_PIM_PACKETS; + PIM_DO_DEBUG_PIM_TRACE; + PIM_DO_DEBUG_IGMP_EVENTS; + PIM_DO_DEBUG_IGMP_PACKETS; + PIM_DO_DEBUG_IGMP_TRACE; + PIM_DO_DEBUG_ZEBRA; +#endif + +#ifdef PIM_ZCLIENT_DEBUG + zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)", + zclient_debug ? "ON" : "OFF"); +#endif + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex"); +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_notice("PIM_REPORT_RECV_IFINDEX_MISMATCH: will report sock/recv ifindex mismatch"); +#endif +#endif + +#ifdef PIM_UNEXPECTED_KERNEL_UPCALL + zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); +#endif + +#ifdef HAVE_CLOCK_MONOTONIC + zlog_notice("HAVE_CLOCK_MONOTONIC"); +#else + zlog_notice("!HAVE_CLOCK_MONOTONIC"); +#endif + + while (thread_fetch(master, &thread)) + thread_call(&thread); + + zlog_err("%s %s: thread_fetch() returned NULL, exiting", + __FILE__, __PRETTY_FUNCTION__); + + /* never reached */ + return 0; +} diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c new file mode 100644 index 00000000..fa460e28 --- /dev/null +++ b/pimd/pim_mroute.c @@ -0,0 +1,451 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "log.h" +#include "privs.h" + +#include "pimd.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_macro.h" + +/* GLOBAL VARS */ +extern struct zebra_privs_t pimd_privs; + +static void mroute_read_on(void); + +static int pim_mroute_set(int fd, int enable) +{ + int err; + int opt = enable ? MRT_INIT : MRT_DONE; + socklen_t opt_len = sizeof(opt); + + err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e)); + errno = e; + return -1; + } + +#if 0 + zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok", + __FILE__, __PRETTY_FUNCTION__, + fd, opt); +#endif + + return 0; +} + +int pim_mroute_msg(int fd, const char *buf, int buf_size) +{ + struct interface *ifp; + const struct ip *ip_hdr; + const struct igmpmsg *msg; + const char *upcall; + char src_str[100]; + char grp_str[100]; + + ip_hdr = (const struct ip *) buf; + + /* kernel upcall must have protocol=0 */ + if (ip_hdr->ip_p) { + /* this is not a kernel upcall */ +#ifdef PIM_UNEXPECTED_KERNEL_UPCALL + zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d", + __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size); +#endif + return 0; + } + + msg = (const struct igmpmsg *) buf; + + switch (msg->im_msgtype) { + case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break; + case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break; + case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break; + default: upcall = ""; + } + ifp = pim_if_find_by_vif_index(msg->im_vif); + pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); + pim_inet4_dump("", msg->im_dst, grp_str, sizeof(grp_str)); + + if (msg->im_msgtype == IGMPMSG_WRONGVIF) { + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* + Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. + + RFC 4601 4.8.2. PIM-SSM-Only Routers + + iif is the incoming interface of the packet. + if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + */ + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", + __PRETTY_FUNCTION__, + fd, + src_str, + grp_str, + ifp ? ifp->name : "", + msg->im_vif); + } + + if (!ifp) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + __PRETTY_FUNCTION__, + src_str, grp_str, msg->im_vif); + return -1; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -2; + } + + ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst); + if (!ch) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -3; + } + + /* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + Transitions from NoInfo State + + An (S,G) data packet arrives on interface I, AND + CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an + downstream interface that is in our (S,G) outgoing interface + list. We optimistically assume that we will be the assert + winner for this (S,G), and so we transition to the "I am Assert + Winner" state and perform Actions A1 (below), which will + initiate the assert negotiation for (S,G). + */ + + if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -4; + } + + if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -5; + } + + if (assert_action_a1(ch)) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -6; + } + + return 0; + } /* IGMPMSG_WRONGVIF */ + + zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", + __PRETTY_FUNCTION__, + upcall, + msg->im_msgtype, + ip_hdr->ip_p, + fd, + src_str, + grp_str, + ifp ? ifp->name : "", + msg->im_vif); + + return 0; +} + +static int mroute_read_msg(int fd) +{ + const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); + char buf[1000]; + int rd; + + if (((int) sizeof(buf)) < msg_min_size) { + zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d", + __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); + return -1; + } + + rd = read(fd, buf, sizeof(buf)); + if (rd < 0) { + zlog_warn("%s: failure reading fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + return -2; + } + + if (rd < msg_min_size) { + zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d", + __PRETTY_FUNCTION__, fd, rd, msg_min_size); + return -3; + } + + return pim_mroute_msg(fd, buf, rd); +} + +static int mroute_read(struct thread *t) +{ + int fd; + int result; + + zassert(t); + zassert(!THREAD_ARG(t)); + + fd = THREAD_FD(t); + zassert(fd == qpim_mroute_socket_fd); + + result = mroute_read_msg(fd); + + /* Keep reading */ + qpim_mroute_socket_reader = 0; + mroute_read_on(); + + return result; +} + +static void mroute_read_on() +{ + zassert(!qpim_mroute_socket_reader); + zassert(PIM_MROUTE_IS_ENABLED); + + THREAD_READ_ON(master, qpim_mroute_socket_reader, + mroute_read, 0, qpim_mroute_socket_fd); +} + +static void mroute_read_off() +{ + THREAD_OFF(qpim_mroute_socket_reader); +} + +int pim_mroute_socket_enable() +{ + int fd; + + if (PIM_MROUTE_IS_ENABLED) + return -1; + + if ( pimd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("pim_mroute_socket_enable: could not raise privs, %s", + safe_strerror (errno) ); + + fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + + if ( pimd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("pim_mroute_socket_enable: could not lower privs, %s", + safe_strerror (errno) ); + + if (fd < 0) { + zlog_warn("Could not create mroute socket: errno=%d: %s", + errno, safe_strerror(errno)); + return -2; + } + + if (pim_mroute_set(fd, 1)) { + zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return -3; + } + + qpim_mroute_socket_fd = fd; + qpim_mroute_socket_creation = pim_time_monotonic_sec(); + mroute_read_on(); + + zassert(PIM_MROUTE_IS_ENABLED); + + return 0; +} + +int pim_mroute_socket_disable() +{ + if (PIM_MROUTE_IS_DISABLED) + return -1; + + if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { + zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", + qpim_mroute_socket_fd, errno, safe_strerror(errno)); + return -2; + } + + if (close(qpim_mroute_socket_fd)) { + zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", + qpim_mroute_socket_fd, errno, safe_strerror(errno)); + return -3; + } + + mroute_read_off(); + qpim_mroute_socket_fd = -1; + + zassert(PIM_MROUTE_IS_DISABLED); + + return 0; +} + +/* + For each network interface (e.g., physical or a virtual tunnel) that + would be used for multicast forwarding, a corresponding multicast + interface must be added to the kernel. + */ +int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr) +{ + struct vifctl vc; + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + memset(&vc, 0, sizeof(vc)); + vc.vifc_vifi = vif_index; + vc.vifc_flags = 0; + vc.vifc_threshold = PIM_MROUTE_MIN_TTL; + vc.vifc_rate_limit = 0; + memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr)); + +#ifdef PIM_DVMRP_TUNNEL + if (vc.vifc_flags & VIFF_TUNNEL) { + memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr)); + } +#endif + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); + if (err) { + char ifaddr_str[100]; + int e = errno; + + pim_inet4_dump("", ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, vif_index, ifaddr_str, + e, safe_strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_del_vif(int vif_index) +{ + struct vifctl vc; + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + memset(&vc, 0, sizeof(vc)); + vc.vifc_vifi = vif_index; + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, vif_index, + e, safe_strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_add(struct mfcctl *mc) +{ + int err; + + qpim_mroute_add_last = pim_time_monotonic_sec(); + ++qpim_mroute_add_events; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, + mc, sizeof(*mc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, + e, safe_strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_del(struct mfcctl *mc) +{ + int err; + + qpim_mroute_del_last = pim_time_monotonic_sec(); + ++qpim_mroute_del_events; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, + e, safe_strerror(e)); + errno = e; + return -2; + } + + return 0; +} diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h new file mode 100644 index 00000000..350b1e37 --- /dev/null +++ b/pimd/pim_mroute.h @@ -0,0 +1,173 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MROUTE_H +#define PIM_MROUTE_H + +/* + For msghdr.msg_control in Solaris 10 +*/ +#ifndef _XPG4_2 +#define _XPG4_2 +#endif +#ifndef __EXTENSIONS__ +#define __EXTENSIONS__ +#endif + +#include +#ifdef HAVE_NETINET_IP_MROUTE_H +#include +#endif + +#define PIM_MROUTE_MIN_TTL (1) + +/* + Below: from +*/ + +#ifndef MAXVIFS +#define MAXVIFS (32) +#endif + +#ifndef SIOCGETVIFCNT +#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ +#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) +#define SIOCGETRPF (SIOCPROTOPRIVATE+2) +#endif + +#ifndef MRT_INIT +#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 */ +#endif + +#ifndef HAVE_VIFI_T +typedef unsigned short vifi_t; +#endif + +#ifndef HAVE_STRUCT_VIFCTL +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 */ +}; +#endif + +#ifndef HAVE_STRUCT_MFCCTL +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; +}; +#endif + +/* + * Group count retrieval for mrouted + */ +/* + struct sioc_sg_req sgreq; + memset(&sgreq, 0, sizeof(sgreq)); + memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src)); + memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp)); + ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq); + */ +#ifndef HAVE_STRUCT_SIOC_SG_REQ +struct sioc_sg_req { + struct in_addr src; + struct in_addr grp; + unsigned long pktcnt; + unsigned long bytecnt; + unsigned long wrong_if; +}; +#endif + +/* + * To get vif packet counts + */ +/* + struct sioc_vif_req vreq; + memset(&vreq, 0, sizeof(vreq)); + vreq.vifi = vif_index; + ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq); + */ +#ifndef HAVE_STRUCT_SIOC_VIF_REQ +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 */ +}; +#endif + +/* + * Pseudo messages used by mrouted + */ +#ifndef IGMPMSG_NOCACHE +#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 + +#ifndef HAVE_STRUCT_IGMPMSG +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; +}; +#endif + +/* + Above: from +*/ + +int pim_mroute_socket_enable(void); +int pim_mroute_socket_disable(void); + +int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr); +int pim_mroute_del_vif(int vif_index); + +int pim_mroute_add(struct mfcctl *mc); +int pim_mroute_del(struct mfcctl *mc); + +int pim_mroute_msg(int fd, const char *buf, int buf_size); + +#endif /* PIM_MROUTE_H */ diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c new file mode 100644 index 00000000..8ead7ce6 --- /dev/null +++ b/pimd/pim_msg.c @@ -0,0 +1,106 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_msg.h" +#include "pim_util.h" + +void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, + uint8_t pim_msg_type) +{ + uint16_t checksum; + + zassert(pim_msg_size >= PIM_PIM_MIN_LEN); + + /* + * Write header + */ + + *(uint8_t *) PIM_MSG_HDR_OFFSET_VERSION(pim_msg) = (PIM_PROTO_VERSION << 4) | pim_msg_type; + *(uint8_t *) PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) = 0; + + /* + * Compute checksum + */ + + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; + checksum = in_cksum(pim_msg, pim_msg_size); + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum; +} + +uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_UCAST_SIZE = 6; + + if (buf_size < ENCODED_IPV4_UCAST_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + memcpy(buf+2, &addr, sizeof(struct in_addr)); + + return buf + ENCODED_IPV4_UCAST_SIZE; +} + +uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_GROUP_SIZE = 8; + + if (buf_size < ENCODED_IPV4_GROUP_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + buf[2] = '\0'; /* reserved */ + buf[3] = 32; /* mask len */ + memcpy(buf+4, &addr, sizeof(struct in_addr)); + + return buf + ENCODED_IPV4_GROUP_SIZE; +} + +uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_SOURCE_SIZE = 8; + + if (buf_size < ENCODED_IPV4_SOURCE_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */ + buf[3] = 32; /* mask len */ + memcpy(buf+4, &addr, sizeof(struct in_addr)); + + return buf + ENCODED_IPV4_SOURCE_SIZE; +} diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h new file mode 100644 index 00000000..a884fc84 --- /dev/null +++ b/pimd/pim_msg.h @@ -0,0 +1,52 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MSG_H +#define PIM_MSG_H + +#include + +/* + Number Description + ---------- ------------------ + 0 Reserved + 1 IP (IP version 4) + 2 IP6 (IP version 6) + + From: + http://www.iana.org/assignments/address-family-numbers +*/ +#define PIM_MSG_ADDRESS_FAMILY_IPV4 (1) + +void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, + uint8_t pim_msg_type); +uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, + int buf_size, + struct in_addr addr); +uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, + int buf_size, + struct in_addr addr); +uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, + int buf_size, + struct in_addr addr); + +#endif /* PIM_MSG_H */ diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c new file mode 100644 index 00000000..9404cec1 --- /dev/null +++ b/pimd/pim_neighbor.c @@ -0,0 +1,719 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_neighbor.h" +#include "pim_time.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_pim.h" +#include "pim_upstream.h" +#include "pim_ifchannel.h" + +static void dr_election_by_addr(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) { + pim_ifp->pim_dr_addr = neigh->source_addr; + } + } +} + +static void dr_election_by_pri(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + uint32_t dr_pri; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + dr_pri = pim_ifp->pim_dr_priority; + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: dr pri %u on interface %s", + __PRETTY_FUNCTION__, + dr_pri, ifp->name); + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("%s: neigh pri %u addr %x if dr addr %x", + __PRETTY_FUNCTION__, + neigh->dr_priority, + ntohl(neigh->source_addr.s_addr), + ntohl(pim_ifp->pim_dr_addr.s_addr)); + } + if ( + (neigh->dr_priority > dr_pri) || + ( + (neigh->dr_priority == dr_pri) && + (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) + ) + ) { + pim_ifp->pim_dr_addr = neigh->source_addr; + dr_pri = neigh->dr_priority; + } + } +} + +/* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ +void pim_if_dr_election(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct in_addr old_dr_addr; + + ++pim_ifp->pim_dr_election_count; + + old_dr_addr = pim_ifp->pim_dr_addr; + + if (pim_ifp->pim_dr_num_nondrpri_neighbors) { + dr_election_by_addr(ifp); + } + else { + dr_election_by_pri(ifp); + } + + /* DR changed ? */ + if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char dr_old_str[100]; + char dr_new_str[100]; + pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); + zlog_debug("%s: DR was %s now is %s on interface %s", + __PRETTY_FUNCTION__, + dr_old_str, dr_new_str, ifp->name); + } + + pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ + ++pim_ifp->pim_dr_election_changes; + pim_if_update_join_desired(pim_ifp); + pim_if_update_could_assert(ifp); + pim_if_update_assert_tracking_desired(ifp); + } +} + +static void update_dr_priority(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint32_t dr_priority) +{ + pim_hello_options will_set_pri; /* boolean */ + pim_hello_options bit_flip; /* boolean */ + pim_hello_options pri_change; /* boolean */ + + will_set_pri = PIM_OPTION_IS_SET(hello_options, + PIM_OPTION_MASK_DR_PRIORITY); + + bit_flip = + ( + will_set_pri != + PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) + ); + + if (bit_flip) { + struct pim_interface *pim_ifp = neigh->interface->info; + + /* update num. of neighbors without dr_pri */ + + if (will_set_pri) { + --pim_ifp->pim_dr_num_nondrpri_neighbors; + } + else { + ++pim_ifp->pim_dr_num_nondrpri_neighbors; + } + } + + pri_change = + ( + bit_flip + || + (neigh->dr_priority != dr_priority) + ); + + if (will_set_pri) { + neigh->dr_priority = dr_priority; + } + else { + neigh->dr_priority = 0; /* cosmetic unset */ + } + + if (pri_change) { + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(neigh->interface); // router's own DR Priority changes + } +} + +static int on_neighbor_timer(struct thread *t) +{ + struct pim_neighbor *neigh; + struct interface *ifp; + char msg[100]; + + zassert(t); + neigh = THREAD_ARG(t); + zassert(neigh); + + ifp = neigh->interface; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s", + neigh->holdtime, src_str, ifp->name); + } + + neigh->t_expire_timer = 0; + + snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime); + pim_neighbor_delete(ifp, neigh, msg); + + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(ifp); // neighbor times out + + return 0; +} + +static void neighbor_timer_off(struct pim_neighbor *neigh) +{ + if (PIM_DEBUG_PIM_TRACE) { + if (neigh->t_expire_timer) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("%s: cancelling timer for neighbor %s on %s", + __PRETTY_FUNCTION__, + src_str, neigh->interface->name); + } + } + THREAD_OFF(neigh->t_expire_timer); + zassert(!neigh->t_expire_timer); +} + +void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) +{ + neigh->holdtime = holdtime; + + neighbor_timer_off(neigh); + + /* + 0xFFFF is request for no holdtime + */ + if (neigh->holdtime == 0xFFFF) { + return; + } + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("%s: starting %u sec timer for neighbor %s on %s", + __PRETTY_FUNCTION__, + neigh->holdtime, src_str, neigh->interface->name); + } + + THREAD_TIMER_ON(master, neigh->t_expire_timer, + on_neighbor_timer, + neigh, neigh->holdtime); +} + +static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + char src_str[100]; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); + if (!neigh) { + zlog_err("%s: PIM XMALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*neigh)); + return 0; + } + + neigh->creation = pim_time_monotonic_sec(); + neigh->source_addr = source_addr; + neigh->hello_options = hello_options; + neigh->propagation_delay_msec = propagation_delay; + neigh->override_interval_msec = override_interval; + neigh->dr_priority = dr_priority; + neigh->generation_id = generation_id; + neigh->prefix_list = addr_list; + neigh->t_expire_timer = 0; + neigh->interface = ifp; + + pim_neighbor_timer_reset(neigh, holdtime); + + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + + if (PIM_DEBUG_PIM_EVENTS) { + zlog_debug("%s: creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s", + src_str, ifp->name); + + if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) { + pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec; + } + if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) { + pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { + /* update num. of neighbors without hello option lan_delay */ + ++pim_ifp->pim_number_of_nonlandelay_neighbors; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_DR_PRIORITY)) { + /* update num. of neighbors without hello option dr_pri */ + ++pim_ifp->pim_dr_num_nondrpri_neighbors; + } + + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(neigh->interface); // new neighbor -- should not trigger dr election... + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + To allow new or rebooting routers to learn of PIM neighbors quickly, + when a Hello message is received from a new neighbor, or a Hello + message with a new GenID is received from an existing neighbor, a + new Hello message should be sent on this interface after a + randomized delay between 0 and Triggered_Hello_Delay. + */ + pim_hello_restart_triggered(neigh->interface); + + return neigh; +} + +static void delete_prefix_list(struct pim_neighbor *neigh) +{ + if (neigh->prefix_list) { + +#ifdef DUMP_PREFIX_LIST + struct listnode *p_node; + struct prefix *p; + char addr_str[10]; + int list_size = neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1; + int i = 0; + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) { + pim_inet4_dump("", p->u.prefix4, addr_str, sizeof(addr_str)); + zlog_debug("%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]", + __PRETTY_FUNCTION__, + (unsigned) neigh, (unsigned) neigh->prefix_list, (unsigned) p, + addr_str, i, list_size); + ++i; + } +#endif + + list_delete(neigh->prefix_list); + neigh->prefix_list = 0; + } +} + +void pim_neighbor_free(struct pim_neighbor *neigh) +{ + zassert(!neigh->t_expire_timer); + + delete_prefix_list(neigh); + + XFREE(MTYPE_PIM_NEIGHBOR, neigh); +} + +struct pim_neighbor *pim_neighbor_find(struct interface *ifp, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if (source_addr.s_addr == neigh->source_addr.s_addr) { + return neigh; + } + } + + return 0; +} + +struct pim_neighbor *pim_neighbor_add(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + + neigh = pim_neighbor_new(ifp, source_addr, + hello_options, + holdtime, + propagation_delay, + override_interval, + dr_priority, + generation_id, + addr_list); + if (!neigh) { + return 0; + } + + pim_ifp = ifp->info; + zassert(pim_ifp); + + listnode_add(pim_ifp->pim_neighbor_list, neigh); + + return neigh; +} + +static uint16_t +find_neighbors_next_highest_propagation_delay_msec(struct interface *ifp, + struct pim_neighbor *highest_neigh) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct pim_neighbor *neigh; + uint16_t next_highest_delay_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) { + if (neigh == highest_neigh) + continue; + if (neigh->propagation_delay_msec > next_highest_delay_msec) + next_highest_delay_msec = neigh->propagation_delay_msec; + } + + return next_highest_delay_msec; +} + +static uint16_t +find_neighbors_next_highest_override_interval_msec(struct interface *ifp, + struct pim_neighbor *highest_neigh) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct pim_neighbor *neigh; + uint16_t next_highest_interval_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + next_highest_interval_msec = pim_ifp->pim_override_interval_msec; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) { + if (neigh == highest_neigh) + continue; + if (neigh->override_interval_msec > next_highest_interval_msec) + next_highest_interval_msec = neigh->override_interval_msec; + } + + return next_highest_interval_msec; +} + +void pim_neighbor_delete(struct interface *ifp, + struct pim_neighbor *neigh, + const char *delete_message) +{ + struct pim_interface *pim_ifp; + char src_str[100]; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s", + src_str, ifp->name, delete_message); + + neighbor_timer_off(neigh); + + pim_if_assert_on_neighbor_down(ifp, neigh->source_addr); + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { + /* update num. of neighbors without hello option lan_delay */ + + --pim_ifp->pim_number_of_nonlandelay_neighbors; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_DR_PRIORITY)) { + /* update num. of neighbors without dr_pri */ + + --pim_ifp->pim_dr_num_nondrpri_neighbors; + } + + zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec); + zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec); + + if (pim_if_lan_delay_enabled(ifp)) { + + /* will delete a neighbor with highest propagation delay? */ + if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) { + /* then find the next highest propagation delay */ + pim_ifp->pim_neighbors_highest_propagation_delay_msec = + find_neighbors_next_highest_propagation_delay_msec(ifp, neigh); + } + + /* will delete a neighbor with highest override interval? */ + if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) { + /* then find the next highest propagation delay */ + pim_ifp->pim_neighbors_highest_override_interval_msec = + find_neighbors_next_highest_override_interval_msec(ifp, neigh); + } + } + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: deleting PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + listnode_delete(pim_ifp->pim_neighbor_list, neigh); + + pim_neighbor_free(neigh); +} + +void pim_neighbor_delete_all(struct interface *ifp, + const char *delete_message) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct listnode *neigh_nextnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node, + neigh_nextnode, neigh)) { + pim_neighbor_delete(ifp, neigh, delete_message); + } +} + +struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh, + struct in_addr addr) +{ + struct listnode *node; + struct prefix *p; + + if (!neigh->prefix_list) + return 0; + + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) { + if (p->family == AF_INET) { + if (addr.s_addr == p->u.prefix4.s_addr) { + return p; + } + } + } + + return 0; +} + +/* + RFC 4601: 4.3.4. Maintaining Secondary Address Lists + + All the advertised secondary addresses in received Hello messages + must be checked against those previously advertised by all other + PIM neighbors on that interface. If there is a conflict and the + same secondary address was previously advertised by another + neighbor, then only the most recently received mapping MUST be + maintained, and an error message SHOULD be logged to the + administrator in a rate-limited manner. +*/ +static void delete_from_neigh_addr(struct interface *ifp, + struct list *addr_list, + struct in_addr neigh_addr) +{ + struct listnode *addr_node; + struct prefix *addr; + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + zassert(addr_list); + + /* + Scan secondary address list + */ + for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node, + addr)) { + struct listnode *neigh_node; + struct pim_neighbor *neigh; + + if (addr->family != AF_INET) + continue; + + /* + Scan neighbors + */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, + neigh)) { + { + struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4); + if (p) { + char addr_str[100]; + char this_neigh_str[100]; + char other_neigh_str[100]; + + pim_inet4_dump("", addr->u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", neigh_addr, this_neigh_str, sizeof(this_neigh_str)); + pim_inet4_dump("", neigh->source_addr, other_neigh_str, sizeof(other_neigh_str)); + + zlog_info("secondary addr %s recvd from neigh %s deleted from neigh %s on %s", + addr_str, this_neigh_str, other_neigh_str, ifp->name); + + listnode_delete(neigh->prefix_list, p); + prefix_free(p); + } + } + + } /* scan neighbors */ + + } /* scan addr list */ + +} + +void pim_neighbor_update(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint16_t holdtime, + uint32_t dr_priority, + struct list *addr_list) +{ + struct pim_interface *pim_ifp = neigh->interface->info; + + /* Received holdtime ? */ + if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { + pim_neighbor_timer_reset(neigh, holdtime); + } + else { + pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); + } + +#ifdef DUMP_PREFIX_LIST + zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d", + __PRETTY_FUNCTION__, + (unsigned) neigh->prefix_list, + neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1, + (unsigned) addr_list, + addr_list ? (int) listcount(addr_list) : -1); +#endif + + if (neigh->prefix_list == addr_list) { + if (addr_list) { + zlog_err("%s: internal error: trying to replace same prefix list=%p", + __PRETTY_FUNCTION__, (void *) addr_list); + } + } + else { + /* Delete existing secondary address list */ + delete_prefix_list(neigh); + } + + if (addr_list) { + delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr); + } + + /* Replace secondary address list */ + neigh->prefix_list = addr_list; + + update_dr_priority(neigh, + hello_options, + dr_priority); + /* + Copy flags + */ + neigh->hello_options = hello_options; +} diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h new file mode 100644 index 00000000..8f19c750 --- /dev/null +++ b/pimd/pim_neighbor.h @@ -0,0 +1,74 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_NEIGHBOR_H +#define PIM_NEIGHBOR_H + +#include + +#include "if.h" +#include "linklist.h" + +#include "pim_tlv.h" + +struct pim_neighbor { + int64_t creation; /* timestamp of creation */ + struct in_addr source_addr; + pim_hello_options hello_options; + uint16_t holdtime; + uint16_t propagation_delay_msec; + uint16_t override_interval_msec; + uint32_t dr_priority; + uint32_t generation_id; + struct list *prefix_list; /* list of struct prefix */ + struct thread *t_expire_timer; + struct interface *interface; +}; + +void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime); +void pim_neighbor_free(struct pim_neighbor *neigh); +struct pim_neighbor *pim_neighbor_find(struct interface *ifp, + struct in_addr source_addr); +struct pim_neighbor *pim_neighbor_add(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list); +void pim_neighbor_delete(struct interface *ifp, + struct pim_neighbor *neigh, + const char *delete_message); +void pim_neighbor_delete_all(struct interface *ifp, + const char *delete_message); +void pim_neighbor_update(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint16_t holdtime, + uint32_t dr_priority, + struct list *addr_list); +struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh, + struct in_addr addr); +void pim_if_dr_election(struct interface *ifp); + +#endif /* PIM_NEIGHBOR_H */ diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c new file mode 100644 index 00000000..1aaece3f --- /dev/null +++ b/pimd/pim_oil.c @@ -0,0 +1,140 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "memory.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_oil.h" +#include "pim_str.h" +#include "pim_iface.h" + +void pim_channel_oil_free(struct channel_oil *c_oil) +{ + XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil); +} + +static void pim_channel_oil_delete(struct channel_oil *c_oil) +{ + /* + notice that listnode_delete() can't be moved + into pim_channel_oil_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(qpim_channel_oil_list, c_oil); + + pim_channel_oil_free(c_oil); +} + +static struct channel_oil *channel_oil_new(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + struct interface *ifp_in; + + ifp_in = pim_if_find_by_vif_index(input_vif_index); + if (!ifp_in) { + /* warning only */ + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + __PRETTY_FUNCTION__, + source_str, group_str, input_vif_index); + } + + c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); + if (!c_oil) { + zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); + return 0; + } + + c_oil->oil.mfcc_mcastgrp = group_addr; + c_oil->oil.mfcc_origin = source_addr; + c_oil->oil.mfcc_parent = input_vif_index; + c_oil->oil_ref_count = 1; + + zassert(c_oil->oil_size == 0); + + return c_oil; +} + +static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + + c_oil = channel_oil_new(group_addr, source_addr, input_vif_index); + if (!c_oil) { + zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); + return 0; + } + + listnode_add(qpim_channel_oil_list, c_oil); + + return c_oil; +} + +static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr, + struct in_addr source_addr) +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) && + (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr)) + return c_oil; + } + + return 0; +} + +struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + + c_oil = pim_find_channel_oil(group_addr, source_addr); + if (c_oil) { + ++c_oil->oil_ref_count; + return c_oil; + } + + return pim_add_channel_oil(group_addr, source_addr, input_vif_index); +} + +void pim_channel_oil_del(struct channel_oil *c_oil) +{ + --c_oil->oil_ref_count; + + if (c_oil->oil_ref_count < 1) { + pim_channel_oil_delete(c_oil); + } +} diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h new file mode 100644 index 00000000..1753545a --- /dev/null +++ b/pimd/pim_oil.h @@ -0,0 +1,53 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_OIL_H +#define PIM_OIL_H + +#include "pim_mroute.h" + +#define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) /* bitmask 1 */ +#define PIM_OIF_FLAG_PROTO_PIM (1 << 1) /* bitmask 2 */ +#define PIM_OIF_FLAG_PROTO_ANY (3) /* bitmask (1 | 2) */ + +/* + qpim_channel_oil_list holds a list of struct channel_oil. + + Each channel_oil.oil is used to control an (S,G) entry in the Kernel + Multicast Forwarding Cache. +*/ + +struct channel_oil { + struct mfcctl oil; + int oil_size; + int oil_ref_count; + time_t oif_creation[MAXVIFS]; + uint32_t oif_flags[MAXVIFS]; +}; + +void pim_channel_oil_free(struct channel_oil *c_oil); +struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index); +void pim_channel_oil_del(struct channel_oil *c_oil); + +#endif /* PIM_OIL_H */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c new file mode 100644 index 00000000..f6f4c953 --- /dev/null +++ b/pimd/pim_pim.c @@ -0,0 +1,742 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "thread.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_sock.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_tlv.h" +#include "pim_neighbor.h" +#include "pim_hello.h" +#include "pim_join.h" +#include "pim_assert.h" +#include "pim_msg.h" +#include "pim_rand.h" + +static int on_pim_hello_send(struct thread *t); +static int pim_hello_send(struct interface *ifp, + uint16_t holdtime); + +static void sock_close(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_TRACE) { + if (pim_ifp->t_pim_sock_read) { + zlog_debug("Cancelling READ event for PIM socket fd=%d on interface %s", + pim_ifp->pim_sock_fd, + ifp->name); + } + } + THREAD_OFF(pim_ifp->t_pim_sock_read); + + if (PIM_DEBUG_PIM_TRACE) { + if (pim_ifp->t_pim_hello_timer) { + zlog_debug("Cancelling PIM hello timer for interface %s", + ifp->name); + } + } + THREAD_OFF(pim_ifp->t_pim_hello_timer); + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Deleting PIM socket fd=%d on interface %s", + pim_ifp->pim_sock_fd, ifp->name); + } + + if (close(pim_ifp->pim_sock_fd)) { + zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s", + pim_ifp->pim_sock_fd, ifp->name, + errno, safe_strerror(errno)); + } + + pim_ifp->pim_sock_fd = -1; + pim_ifp->pim_sock_creation = 0; + + zassert(pim_ifp->pim_sock_fd < 0); + zassert(!pim_ifp->t_pim_sock_read); + zassert(!pim_ifp->t_pim_hello_timer); + zassert(!pim_ifp->pim_sock_creation); +} + +void pim_sock_delete(struct interface *ifp, const char *delete_message) +{ + zlog_info("PIM INTERFACE DOWN: on interface %s: %s", + ifp->name, delete_message); + + if (!ifp->info) { + zlog_err("%s: %s: but PIM not enabled on interface %s (!)", + __PRETTY_FUNCTION__, delete_message, ifp->name); + return; + } + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Before an interface goes down or changes primary IP address, a Hello + message with a zero HoldTime should be sent immediately (with the + old IP address if the IP address changed). + */ + pim_hello_send(ifp, 0 /* zero-sec holdtime */); + + pim_neighbor_delete_all(ifp, delete_message); + + sock_close(ifp); +} + +int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) +{ + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + char src_str[100]; + char dst_str[100]; + uint8_t *pim_msg; + int pim_msg_len; + uint8_t pim_version; + uint8_t pim_type; + uint16_t pim_checksum; /* received checksum */ + uint16_t checksum; /* computed checksum */ + struct pim_neighbor *neigh; + + if (!ifp->info) { + zlog_warn("%s: PIM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + if (len < sizeof(*ip_hdr)) { + zlog_warn("PIM packet size=%zu shorter than minimum=%zu", + len, sizeof(*ip_hdr)); + return -1; + } + + ip_hdr = (struct ip *) buf; + + pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, dst_str, sizeof(dst_str)); + + ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ + + if (PIM_DEBUG_PIM_PACKETS) { + zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", + src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p); + } + + if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) { + zlog_warn("IP packet protocol=%d is not PIM=%d", + ip_hdr->ip_p, PIM_IP_PROTO_PIM); + return -1; + } + + if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { + zlog_warn("IP packet header size=%zu shorter than minimum=%d", + ip_hlen, PIM_IP_HEADER_MIN_LEN); + return -1; + } + if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { + zlog_warn("IP packet header size=%zu greater than maximum=%d", + ip_hlen, PIM_IP_HEADER_MAX_LEN); + return -1; + } + + pim_msg = buf + ip_hlen; + pim_msg_len = len - ip_hlen; + + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len); + } + + if (pim_msg_len < PIM_PIM_MIN_LEN) { + zlog_warn("PIM message size=%d shorter than minimum=%d", + pim_msg_len, PIM_PIM_MIN_LEN); + return -1; + } + + pim_version = PIM_MSG_HDR_GET_VERSION(pim_msg); + pim_type = PIM_MSG_HDR_GET_TYPE(pim_msg); + + if (pim_version != PIM_PROTO_VERSION) { + zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d", + ifp->name, pim_version); + return -1; + } + + /* save received checksum */ + pim_checksum = PIM_MSG_HDR_GET_CHECKSUM(pim_msg); + + /* for computing checksum */ + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; + + checksum = in_cksum(pim_msg, pim_msg_len); + if (checksum != pim_checksum) { + zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", + ifp->name, pim_checksum, checksum); + return -1; + } + + if (PIM_DEBUG_PIM_PACKETS) { + zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x", + src_str, dst_str, ifp->name, ip_hdr->ip_ttl, + pim_version, pim_type, pim_msg_len, checksum); + } + + if (pim_type == PIM_MSG_TYPE_HELLO) { + int result = pim_hello_recv(ifp, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + if (!result) { + pim_if_dr_election(ifp); /* PIM Hello message is received */ + } + return result; + } + + neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); + if (!neigh) { + zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + return -1; + } + + switch (pim_type) { + case PIM_MSG_TYPE_JOIN_PRUNE: + return pim_joinprune_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + case PIM_MSG_TYPE_ASSERT: + return pim_assert_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + default: + zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + } + + return -1; +} + +static void pim_sock_read_on(struct interface *ifp); + +static int pim_sock_read(struct thread *t) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int fd; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + uint8_t buf[PIM_PIM_BUFSIZE_READ]; + int len; + int ifindex = -1; + int result = -1; /* defaults to bad */ + + zassert(t); + + ifp = THREAD_ARG(t); + zassert(ifp); + + fd = THREAD_FD(t); + + pim_ifp = ifp->info; + zassert(pim_ifp); + + zassert(fd == pim_ifp->pim_sock_fd); + + len = pim_socket_recvfromto(fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + goto done; + } + + if (PIM_DEBUG_PIM_PACKETS) { + char from_str[100]; + char to_str[100]; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", + len, from_str, to_str, fd, ifindex, ifp->ifindex); + } + + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + pim_pkt_dump(__PRETTY_FUNCTION__, buf, len); + } + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + /* ifindex sanity check */ + if (ifindex != (int) ifp->ifindex) { + char from_str[100]; + char to_str[100]; + struct interface *recv_ifp; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + recv_ifp = if_lookup_by_index(ifindex); + if (recv_ifp) { + zassert(ifindex == (int) recv_ifp->ifindex); + } + +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", + from_str, to_str, fd, + ifindex, recv_ifp ? recv_ifp->name : "", + ifp->ifindex, ifp->name); +#endif + goto done; + } +#endif + + int fail = pim_pim_packet(ifp, buf, len); + if (fail) { + zlog_warn("%s: pim_pim_packet() return=%d", + __PRETTY_FUNCTION__, fail); + goto done; + } + + result = 0; /* good */ + + done: + pim_sock_read_on(ifp); + + if (result) { + ++pim_ifp->pim_ifstat_hello_recvfail; + } + + return result; +} + +static void pim_sock_read_on(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(ifp->info); + + pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Scheduling READ event on PIM socket fd=%d", + pim_ifp->pim_sock_fd); + } + pim_ifp->t_pim_sock_read = 0; + zassert(!pim_ifp->t_pim_sock_read); + THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp, + pim_ifp->pim_sock_fd); +} + +static int pim_sock_open(struct in_addr ifaddr, int ifindex) +{ + int fd; + + fd = pim_socket_mcast(IPPROTO_PIM, ifaddr, 0 /* loop=false */); + if (fd < 0) + return -1; + + if (pim_socket_join(fd, qpim_all_pim_routers_addr, ifaddr, ifindex)) { + return -2; + } + + return fd; +} + +void pim_ifstat_reset(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + return; + } + + pim_ifp->pim_ifstat_start = pim_time_monotonic_sec(); + pim_ifp->pim_ifstat_hello_sent = 0; + pim_ifp->pim_ifstat_hello_sendfail = 0; + pim_ifp->pim_ifstat_hello_recv = 0; + pim_ifp->pim_ifstat_hello_recvfail = 0; +} + +void pim_sock_reset(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(ifp->info); + + pim_ifp = ifp->info; + + pim_ifp->primary_address = pim_find_primary_addr(ifp); + + pim_ifp->pim_sock_fd = -1; + pim_ifp->pim_sock_creation = 0; + pim_ifp->t_pim_sock_read = 0; + + pim_ifp->t_pim_hello_timer = 0; + pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD; + pim_ifp->pim_default_holdtime = -1; /* unset: means 3.5 * pim_hello_period */ + pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY; + pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY; + pim_ifp->pim_propagation_delay_msec = PIM_DEFAULT_PROPAGATION_DELAY_MSEC; + pim_ifp->pim_override_interval_msec = PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; + if (PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION) { + PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options); + } + else { + PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options); + } + + /* neighbors without lan_delay */ + pim_ifp->pim_number_of_nonlandelay_neighbors = 0; + pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0; + pim_ifp->pim_neighbors_highest_override_interval_msec = 0; + + /* DR Election */ + pim_ifp->pim_dr_election_last = 0; /* timestamp */ + pim_ifp->pim_dr_election_count = 0; + pim_ifp->pim_dr_election_changes = 0; + pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */ + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + + pim_ifstat_reset(ifp); +} + +int pim_msg_send(int fd, + struct in_addr dst, + uint8_t *pim_msg, + int pim_msg_size, + const char *ifname) +{ + ssize_t sent; + struct sockaddr_in to; + socklen_t tolen; + + if (PIM_DEBUG_PIM_PACKETS) { + char dst_str[100]; + pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); + zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x", + __PRETTY_FUNCTION__, + dst_str, ifname, pim_msg_size, + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg)); + } + +#if 0 + memset(&to, 0, sizeof(to)); +#endif + to.sin_family = AF_INET; + to.sin_addr = dst; +#if 0 + to.sin_port = htons(0); +#endif + tolen = sizeof(to); + + if (PIM_DEBUG_PIM_PACKETDUMP_SEND) { + pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size); + } + + sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, &to, tolen); + if (sent != (ssize_t) pim_msg_size) { + int e = errno; + char dst_str[100]; + pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + dst_str, ifname, fd, pim_msg_size, + e, safe_strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd", + __PRETTY_FUNCTION__, + dst_str, ifname, fd, + pim_msg_size, sent); + } + return -1; + } + + return 0; +} + +static int hello_send(struct interface *ifp, + uint16_t holdtime) +{ + uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE]; + struct pim_interface *pim_ifp; + int pim_tlv_size; + int pim_msg_size; + + pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_PACKETS || PIM_DEBUG_PIM_HELLO) { + char dst_str[100]; + pim_inet4_dump("", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", + __PRETTY_FUNCTION__, + dst_str, ifp->name, + holdtime, + pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec, + PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options), + pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id, + listcount(ifp->connected)); + } + + pim_tlv_size = pim_hello_build_tlv(ifp->name, + pim_msg + PIM_PIM_MIN_LEN, + sizeof(pim_msg) - PIM_PIM_MIN_LEN, + holdtime, + pim_ifp->pim_dr_priority, + pim_ifp->pim_generation_id, + pim_ifp->pim_propagation_delay_msec, + pim_ifp->pim_override_interval_msec, + PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options), + ifp->connected); + if (pim_tlv_size < 0) { + return -1; + } + + pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; + + zassert(pim_msg_size >= PIM_PIM_MIN_LEN); + zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE); + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_HELLO); + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -2; + } + + return 0; +} + +static int pim_hello_send(struct interface *ifp, + uint16_t holdtime) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (hello_send(ifp, holdtime)) { + ++pim_ifp->pim_ifstat_hello_sendfail; + + zlog_warn("Could not send PIM hello on interface %s", + ifp->name); + return -1; + } + + ++pim_ifp->pim_ifstat_hello_sent; + + return 0; +} + +static void hello_resched(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Rescheduling %d sec hello on interface %s", + pim_ifp->pim_hello_period, ifp->name); + } + THREAD_OFF(pim_ifp->t_pim_hello_timer); + THREAD_TIMER_ON(master, pim_ifp->t_pim_hello_timer, + on_pim_hello_send, + ifp, pim_ifp->pim_hello_period); +} + +/* + Periodic hello timer + */ +static int on_pim_hello_send(struct thread *t) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + + zassert(t); + ifp = THREAD_ARG(t); + zassert(ifp); + + pim_ifp = ifp->info; + + /* + * Schedule next hello + */ + pim_ifp->t_pim_hello_timer = 0; + hello_resched(ifp); + + /* + * Send hello + */ + return pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on an + interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ +void pim_hello_restart_now(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + /* + * Reset next hello timer + */ + hello_resched(ifp); + + /* + * Immediately send hello + */ + pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + To allow new or rebooting routers to learn of PIM neighbors quickly, + when a Hello message is received from a new neighbor, or a Hello + message with a new GenID is received from an existing neighbor, a + new Hello message should be sent on this interface after a + randomized delay between 0 and Triggered_Hello_Delay. + */ +void pim_hello_restart_triggered(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + int triggered_hello_delay_msec; + int random_msec; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay; + + if (pim_ifp->t_pim_hello_timer) { + long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer); + if (remain_msec <= triggered_hello_delay_msec) { + /* Rescheduling hello would increase the delay, then it's faster + to just wait for the scheduled periodic hello. */ + return; + } + + THREAD_OFF(pim_ifp->t_pim_hello_timer); + pim_ifp->t_pim_hello_timer = 0; + } + zassert(!pim_ifp->t_pim_hello_timer); + + random_msec = pim_rand_next(0, triggered_hello_delay_msec); + + if (PIM_DEBUG_PIM_EVENTS) { + zlog_debug("Scheduling %d msec triggered hello on interface %s", + random_msec, ifp->name); + } + + THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer, + on_pim_hello_send, + ifp, random_msec); +} + +int pim_sock_add(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (pim_ifp->pim_sock_fd >= 0) { + zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s", + pim_ifp->pim_sock_fd, ifp->name); + return -1; + } + + ifaddr = pim_ifp->primary_address; + + pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex); + if (pim_ifp->pim_sock_fd < 0) { + zlog_warn("Could not open PIM socket on interface %s", + ifp->name); + return -2; + } + + pim_ifp->t_pim_sock_read = 0; + pim_ifp->pim_sock_creation = pim_time_monotonic_sec(); + + pim_ifp->pim_generation_id = pim_rand() & (int64_t) 0xFFFFFFFF; + + zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", + ifp->name, ifp->ifindex); + + /* + * Start receiving PIM messages + */ + pim_sock_read_on(ifp); + + /* + * Start sending PIM hello's + */ + pim_hello_restart_triggered(ifp); + + return 0; +} diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h new file mode 100644 index 00000000..6be1a3e6 --- /dev/null +++ b/pimd/pim_pim.h @@ -0,0 +1,71 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_PIM_H +#define PIM_PIM_H + +#include + +#include "if.h" + +#define PIM_PIM_BUFSIZE_READ (20000) +#define PIM_PIM_BUFSIZE_WRITE (20000) + +#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20) + +#define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */ +#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */ +#define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */ +#define PIM_DEFAULT_PROPAGATION_DELAY_MSEC (500) /* RFC 4601: 4.11. Timer Values */ +#define PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC (2500) /* RFC 4601: 4.11. Timer Values */ +#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */ +#define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */ + +#define PIM_MSG_TYPE_HELLO (0) +#define PIM_MSG_TYPE_JOIN_PRUNE (3) +#define PIM_MSG_TYPE_ASSERT (5) + +#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg) +#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg) +#define PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) (((char *)(pim_msg)) + 1) +#define PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) (((char *)(pim_msg)) + 2) + +#define PIM_MSG_HDR_GET_VERSION(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_VERSION(pim_msg)) >> 4) +#define PIM_MSG_HDR_GET_TYPE(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_TYPE(pim_msg)) & 0xF) +#define PIM_MSG_HDR_GET_CHECKSUM(pim_msg) (*(uint16_t*) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg)) + +void pim_ifstat_reset(struct interface *ifp); +void pim_sock_reset(struct interface *ifp); +int pim_sock_add(struct interface *ifp); +void pim_sock_delete(struct interface *ifp, const char *delete_message); +void pim_hello_restart_now(struct interface *ifp); +void pim_hello_restart_triggered(struct interface *ifp); + +int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len); + +int pim_msg_send(int fd, + struct in_addr dst, + uint8_t *pim_msg, + int pim_msg_size, + const char *ifname); + +#endif /* PIM_PIM_H */ diff --git a/pimd/pim_rand.c b/pimd/pim_rand.c new file mode 100644 index 00000000..df2a1111 --- /dev/null +++ b/pimd/pim_rand.c @@ -0,0 +1,60 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_rand.h" +#include "pim_time.h" + +/* Quick and dirty random number generator from NUMERICAL RECIPES IN C: + THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5). */ +/* BEWARE: '_qseed_' is assigned! */ +#define QRANDOM(_qseed_) ((_qseed_) = (((_qseed_) * 1664525L) + 1013904223L)) + +static long qpim_rand_seed; + +void pim_rand_init() +{ + qpim_rand_seed = pim_time_monotonic_sec() ^ getpid(); +} + +long pim_rand() +{ + return QRANDOM(qpim_rand_seed); +} + +int pim_rand_next(int min, int max) +{ + long rand; + + assert(min <= max); + + /* FIXME better random generator ? */ + + rand = QRANDOM(qpim_rand_seed); + if (rand < 0) + rand = -rand; + rand = rand % (1 + max - min) + min; + + assert(rand >= min); + assert(rand <= max); + + return rand; +} diff --git a/pimd/pim_rand.h b/pimd/pim_rand.h new file mode 100644 index 00000000..a1df5054 --- /dev/null +++ b/pimd/pim_rand.h @@ -0,0 +1,30 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_RAND_H +#define PIM_RAND_H + +void pim_rand_init(void); +long pim_rand(void); +int pim_rand_next(int min, int max); + +#endif /* PIM_RAND_H */ diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c new file mode 100644 index 00000000..dedc60a5 --- /dev/null +++ b/pimd/pim_rpf.c @@ -0,0 +1,260 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_rpf.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_zlookup.h" +#include "pim_ifchannel.h" + +static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, + struct in_addr addr) +{ + struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + int num_ifindex; + struct interface *ifp; + int first_ifindex; + + num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, + addr, PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + return -1; + } + + first_ifindex = nexthop_tab[0].ifindex; + + if (num_ifindex > 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", + __FILE__, __PRETTY_FUNCTION__, + num_ifindex, addr_str, first_ifindex); + /* debug warning only, do not return */ + } + + ifp = if_lookup_by_index(first_ifindex); + if (!ifp) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find interface for ifindex %d (address %s)", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, addr_str); + return -2; + } + + if (!ifp->info) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", + __PRETTY_FUNCTION__, + ifp->name, first_ifindex, addr_str); + /* debug warning only, do not return */ + } + + if (PIM_DEBUG_PIM_TRACE) { + char nexthop_str[100]; + char addr_str[100]; + pim_inet4_dump("", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", + __FILE__, __PRETTY_FUNCTION__, + nexthop_str, addr_str, + ifp->name, first_ifindex, + nexthop_tab[0].route_metric, + nexthop_tab[0].protocol_distance); + } + + /* update nextop data */ + nexthop->interface = ifp; + nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr; + nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance; + nexthop->mrib_route_metric = nexthop_tab[0].route_metric; + + return 0; +} + +static int nexthop_mismatch(const struct pim_nexthop *nh1, + const struct pim_nexthop *nh2) +{ + return (nh1->interface != nh2->interface) + || + (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr) + || + (nh1->mrib_metric_preference != nh2->mrib_metric_preference) + || + (nh1->mrib_route_metric != nh2->mrib_route_metric); +} + +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, + struct in_addr *old_rpf_addr) +{ + struct in_addr save_rpf_addr; + struct pim_nexthop save_nexthop; + struct pim_rpf *rpf = &up->rpf; + + save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */ + save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */ + + if (pim_nexthop_lookup(&rpf->source_nexthop, + up->source_addr)) { + return PIM_RPF_FAILURE; + } + + rpf->rpf_addr = pim_rpf_find_rpf_addr(up); + if (PIM_INADDR_IS_ANY(rpf->rpf_addr)) { + /* RPF'(S,G) not found */ + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s %s: RPF'(%s,%s) not found: won't send join upstream", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str); + /* warning only */ + } + + /* detect change in pim_nexthop */ + if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) { + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char src_str[100]; + char grp_str[100]; + char nhaddr_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", + nhaddr_str, + rpf->source_nexthop.mrib_metric_preference, + rpf->source_nexthop.mrib_route_metric); + /* warning only */ + } + + pim_upstream_update_join_desired(up); + pim_upstream_update_could_assert(up); + pim_upstream_update_my_assert_metric(up); + } + + /* detect change in RPF_interface(S) */ + if (save_nexthop.interface != rpf->source_nexthop.interface) { + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + save_nexthop.interface ? save_nexthop.interface->name : "", + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); + /* warning only */ + } + + pim_upstream_rpf_interface_changed(up, save_nexthop.interface); + } + + /* detect change in RPF'(S,G) */ + if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) { + + /* return old rpf to caller ? */ + if (old_rpf_addr) + *old_rpf_addr = save_rpf_addr; + + return PIM_RPF_CHANGED; + } + + return PIM_RPF_OK; +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + neighbor RPF'(S,G) { + if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) { + return AssertWinner(S, G, RPF_interface(S) ) + } else { + return NBR( RPF_interface(S), MRIB.next_hop( S ) ) + } + } + + RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data + packets should be coming and to which joins should be sent on the RP + tree and SPT, respectively. +*/ +static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) +{ + struct pim_ifchannel *rpf_ch; + struct pim_neighbor *neigh; + struct in_addr rpf_addr; + + if (!up->rpf.source_nexthop.interface) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + src_str, grp_str); + + rpf_addr.s_addr = PIM_NET_INADDR_ANY; + return rpf_addr; + } + + rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface, + up->source_addr, up->group_addr); + if (rpf_ch) { + if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + return rpf_ch->ifassert_winner; + } + } + + /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */ + + neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface, + up->rpf.source_nexthop.mrib_nexthop_addr); + if (neigh) + rpf_addr = neigh->source_addr; + else + rpf_addr.s_addr = PIM_NET_INADDR_ANY; + + return rpf_addr; +} diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h new file mode 100644 index 00000000..078e89f3 --- /dev/null +++ b/pimd/pim_rpf.h @@ -0,0 +1,36 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_RPF_H +#define PIM_RPF_H + +#include + +#include "pim_upstream.h" +#include "pim_neighbor.h" + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, + struct in_addr addr); +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, + struct in_addr *old_rpf_addr); + +#endif /* PIM_RPF_H */ diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c new file mode 100644 index 00000000..d1350b08 --- /dev/null +++ b/pimd/pim_signals.c @@ -0,0 +1,86 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include +#include "sigevent.h" +#include "memory.h" +#include "log.h" + +#include "pim_signals.h" +#include "pimd.h" + +/* + * Signal handlers + */ + +static void pim_sighup() +{ + zlog_debug ("SIGHUP received, ignoring"); +} + +static void pim_sigint() +{ + zlog_notice("Terminating on signal SIGINT"); + pim_terminate(); + exit(1); +} + +static void pim_sigterm() +{ + zlog_notice("Terminating on signal SIGTERM"); + pim_terminate(); + exit(1); +} + +static void pim_sigusr1() +{ + zlog_debug ("SIGUSR1 received"); + zlog_rotate (NULL); +} + +static struct quagga_signal_t pimd_signals[] = +{ + { + .signal = SIGHUP, + .handler = &pim_sighup, + }, + { + .signal = SIGUSR1, + .handler = &pim_sigusr1, + }, + { + .signal = SIGINT, + .handler = &pim_sigint, + }, + { + .signal = SIGTERM, + .handler = &pim_sigterm, + }, +}; + +void pim_signals_init() +{ + signal_init(master, array_size(pimd_signals), pimd_signals); +} + diff --git a/pimd/pim_signals.h b/pimd/pim_signals.h new file mode 100644 index 00000000..62523c03 --- /dev/null +++ b/pimd/pim_signals.h @@ -0,0 +1,28 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SIGNALS_H +#define PIM_SIGNALS_H + +void pim_signals_init(void); + +#endif /* PIM_SIGNALS_H */ diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c new file mode 100644 index 00000000..4816c567 --- /dev/null +++ b/pimd/pim_sock.c @@ -0,0 +1,389 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_mroute.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "log.h" +#include "privs.h" + +#include "pimd.h" +#include "pim_sock.h" +#include "pim_str.h" +#include "pim_igmp_join.h" + +/* GLOBAL VARS */ +extern struct zebra_privs_t pimd_privs; + +int pim_socket_raw(int protocol) +{ + int fd; + + if ( pimd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("pim_sockek_raw: could not raise privs, %s", + safe_strerror (errno) ); + + fd = socket(AF_INET, SOCK_RAW, protocol); + + if ( pimd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("pim_socket_raw: could not lower privs, %s", + safe_strerror (errno) ); + + if (fd < 0) { + zlog_warn("Could not create raw socket: errno=%d: %s", + errno, safe_strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } + + return fd; +} + +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) +{ + int fd; + + fd = pim_socket_raw(protocol); + if (fd < 0) { + zlog_warn("Could not create multicast socket: errno=%d: %s", + errno, safe_strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } + + /* Needed to obtain destination address from recvmsg() */ + { +#if defined(HAVE_IP_PKTINFO) + /* Linux IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + } +#elif defined(HAVE_IP_RECVDSTADDR) + /* BSD IP_RECVDSTADDR */ + int opt = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + } +#else + zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return PIM_SOCK_ERR_DSTADDR; +#endif + } + + + /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/ + if (protocol == IPPROTO_IGMP) { + char ra[4]; + ra[0] = 148; + ra[1] = 4; + ra[2] = 0; + ra[3] = 0; + if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { + zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_RA; + } + } + + { + int reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &reuse, sizeof(reuse))) { + zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_REUSE; + } + } + + { + const int MTTL = 1; + int ttl = MTTL; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (void *) &ttl, sizeof(ttl))) { + zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", + MTTL, fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_TTL; + } + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (void *) &loop, sizeof(loop))) { + zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", + loop ? "enable" : "disable", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_LOOP; + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (void *) &ifaddr, sizeof(ifaddr))) { + zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_IFACE; + } + + { + long flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_NONBLOCK_GETFL; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_NONBLOCK_SETFL; + } + } + + return fd; +} + +int pim_socket_join(int fd, struct in_addr group, + struct in_addr ifaddr, int ifindex) +{ + int ret; + +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn opt; +#else + struct ip_mreq opt; +#endif + + opt.imr_multiaddr = group; + +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + opt.imr_address = ifaddr; + opt.imr_ifindex = ifindex; +#else + opt.imr_interface = ifaddr; +#endif + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); + if (ret) { + char group_str[100]; + char ifaddr_str[100]; + if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) + sprintf(group_str, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", + fd, group_str, ifaddr_str, errno, safe_strerror(errno)); + return ret; + } + + if (PIM_DEBUG_TRACE) { + char group_str[100]; + char ifaddr_str[100]; + if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) + sprintf(group_str, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + zlog_debug("Socket fd=%d joined group %s on interface address %s", + fd, group_str, ifaddr_str); + } + + return ret; +} + +int pim_socket_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr, + const char *ifname) +{ + if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) { + int e = errno; + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", + __PRETTY_FUNCTION__, + fd, group_str, source_str, ifindex, ifname, + e, safe_strerror(e)); + return -1; + } + + return 0; +} + +int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, + struct sockaddr_in *from, socklen_t *fromlen, + struct sockaddr_in *to, socklen_t *tolen, + int *ifindex) +{ + struct msghdr msgh; + struct cmsghdr *cmsg; + struct iovec iov; + char cbuf[1000]; + int err; + + /* + * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port. + * Use getsockname() to get sin_port. + */ + if (to) { + struct sockaddr_in si; + socklen_t si_len = sizeof(si); + + ((struct sockaddr_in *) to)->sin_family = AF_INET; + + if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) { + ((struct sockaddr_in *) to)->sin_port = ntohs(0); + ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0); + } + else { + ((struct sockaddr_in *) to)->sin_port = si.sin_port; + ((struct sockaddr_in *) to)->sin_addr = si.sin_addr; + } + + if (tolen) + *tolen = sizeof(si); + } + + memset(&msgh, 0, sizeof(struct msghdr)); + iov.iov_base = buf; + iov.iov_len = len; + msgh.msg_control = cbuf; + msgh.msg_controllen = sizeof(cbuf); + msgh.msg_name = from; + msgh.msg_namelen = fromlen ? *fromlen : 0; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_flags = 0; + + err = recvmsg(fd, &msgh, 0); + if (err < 0) + return err; + + if (fromlen) + *fromlen = msgh.msg_namelen; + + for (cmsg = CMSG_FIRSTHDR(&msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + +#ifdef HAVE_IP_PKTINFO + if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { + struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg); + if (to) + ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr; + if (tolen) + *tolen = sizeof(struct sockaddr_in); + if (ifindex) + *ifindex = i->ipi_ifindex; + + if (to && PIM_DEBUG_PACKETS) { + char to_str[100]; + pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to->sin_port)); + } + + break; + } +#endif + +#ifdef HAVE_IP_RECVDSTADDR + if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) { + struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg); + if (to) + ((struct sockaddr_in *) to)->sin_addr = *i; + if (tolen) + *tolen = sizeof(struct sockaddr_in); + + if (to && PIM_DEBUG_PACKETS) { + char to_str[100]; + pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to->sin_port)); + } + + break; + } +#endif + +#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) + if (cmsg->cmsg_type == IP_RECVIF) + if (ifindex) + *ifindex = CMSG_IFINDEX(cmsg); +#endif + + } /* for (cmsg) */ + + return err; /* len */ +} + +int pim_socket_mcastloop_get(int fd) +{ + int loop; + socklen_t loop_len = sizeof(loop); + + if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, &loop_len)) { + int e = errno; + zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + errno = e; + return PIM_SOCK_ERR_LOOP; + } + + return loop; +} + +int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen) +{ + if (getsockname(fd, name, namelen)) { + int e = errno; + zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + errno = e; + return PIM_SOCK_ERR_NAME; + } + + return PIM_SOCK_ERR_NONE; +} diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h new file mode 100644 index 00000000..cfe39ad1 --- /dev/null +++ b/pimd/pim_sock.h @@ -0,0 +1,57 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SOCK_H +#define PIM_SOCK_H + +#include + +#define PIM_SOCK_ERR_NONE (0) /* No error */ +#define PIM_SOCK_ERR_SOCKET (-1) /* socket() */ +#define PIM_SOCK_ERR_RA (-2) /* Router Alert option */ +#define PIM_SOCK_ERR_REUSE (-3) /* Reuse option */ +#define PIM_SOCK_ERR_TTL (-4) /* TTL option */ +#define PIM_SOCK_ERR_LOOP (-5) /* Loopback option */ +#define PIM_SOCK_ERR_IFACE (-6) /* Outgoing interface option */ +#define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */ +#define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */ +#define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */ +#define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */ + +int pim_socket_raw(int protocol); +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop); +int pim_socket_join(int fd, struct in_addr group, + struct in_addr ifaddr, int ifindex); +int pim_socket_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr, + const char *ifname); +int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, + struct sockaddr_in *from, socklen_t *fromlen, + struct sockaddr_in *to, socklen_t *tolen, + int *ifindex); + +int pim_socket_mcastloop_get(int fd); + +int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen); + +#endif /* PIM_SOCK_H */ diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c new file mode 100644 index 00000000..fd1eac06 --- /dev/null +++ b/pimd/pim_ssmpingd.c @@ -0,0 +1,448 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "if.h" +#include "log.h" +#include "memory.h" + +#include "pim_ssmpingd.h" +#include "pim_time.h" +#include "pim_sock.h" +#include "pim_str.h" +#include "pimd.h" + +static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234"; + +enum { + PIM_SSMPINGD_REQUEST = 'Q', + PIM_SSMPINGD_REPLY = 'A' +}; + +static void ssmpingd_read_on(struct ssmpingd_sock *ss); + +void pim_ssmpingd_init() +{ + int result; + + zassert(!qpim_ssmpingd_list); + + result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr); + + zassert(result > 0); +} + +void pim_ssmpingd_destroy() +{ + if (qpim_ssmpingd_list) { + list_free(qpim_ssmpingd_list); + qpim_ssmpingd_list = 0; + } +} + +static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr) +{ + struct listnode *node; + struct ssmpingd_sock *ss; + + if (!qpim_ssmpingd_list) + return 0; + + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) + if (source_addr.s_addr == ss->source_addr.s_addr) + return ss; + + return 0; +} + +static void ssmpingd_free(struct ssmpingd_sock *ss) +{ + XFREE(MTYPE_PIM_SSMPINGD, ss); +} + +static int ssmpingd_socket(struct in_addr addr, int port, int mttl) +{ + struct sockaddr_in sockaddr; + int fd; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + zlog_err("%s: could not create socket: errno=%d: %s", + __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + return -1; + } + + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr = addr; + sockaddr.sin_port = htons(port); + + if (bind(fd, &sockaddr, sizeof(sockaddr))) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", + __PRETTY_FUNCTION__, + fd, addr_str, port, sizeof(sockaddr), + errno, safe_strerror(errno)); + close(fd); + return -1; + } + + /* Needed to obtain destination address from recvmsg() */ + { +#if defined(HAVE_IP_PKTINFO) + /* Linux IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { + zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + } +#elif defined(HAVE_IP_RECVDSTADDR) + /* BSD IP_RECVDSTADDR */ + int opt = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { + zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + } +#else + zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return -1; +#endif + } + + { + int reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &reuse, sizeof(reuse))) { + zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (void *) &mttl, sizeof(mttl))) { + zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + { + int loop = 0; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (void *) &loop, sizeof(loop))) { + zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + loop ? "enable" : "disable", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_LOOP; + } + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (void *) &addr, sizeof(addr))) { + zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + { + long flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + } + + return fd; +} + +static void ssmpingd_delete(struct ssmpingd_sock *ss) +{ + zassert(ss); + zassert(qpim_ssmpingd_list); + + THREAD_OFF(ss->t_sock_read); + + if (close(ss->sock_fd)) { + int e = errno; + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s", + __PRETTY_FUNCTION__, + ss->sock_fd, source_str, e, safe_strerror(e)); + /* warning only */ + } + + listnode_delete(qpim_ssmpingd_list, ss); + ssmpingd_free(ss); +} + +static void ssmpingd_sendto(struct ssmpingd_sock *ss, + const uint8_t *buf, + int len, + struct sockaddr_in to) +{ + socklen_t tolen = sizeof(to); + int sent; + + sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen); + if (sent != len) { + int e = errno; + char to_str[100]; + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + to_str, ntohs(to.sin_port), ss->sock_fd, len, + e, safe_strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to.sin_port), ss->sock_fd, + len, sent); + } + } +} + +static int ssmpingd_read_msg(struct ssmpingd_sock *ss) +{ + struct interface *ifp; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + int ifindex = -1; + uint8_t buf[1000]; + int len; + + ++ss->requests; + + len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno)); + return -1; + } + + ifp = if_lookup_by_index(ifindex); + + if (buf[0] != PIM_SSMPINGD_REQUEST) { + char source_str[100]; + char from_str[100]; + char to_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", + __PRETTY_FUNCTION__, + buf[0], + from_str, ntohs(from.sin_port), + to_str, ntohs(to.sin_port), + ifp ? ifp->name : "", + ifindex, ss->sock_fd, + source_str); + return 0; + } + + if (PIM_DEBUG_SSMPINGD) { + char source_str[100]; + char from_str[100]; + char to_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", + __PRETTY_FUNCTION__, + from_str, ntohs(from.sin_port), + to_str, ntohs(to.sin_port), + ifp ? ifp->name : "", + ifindex, ss->sock_fd, + source_str); + } + + buf[0] = PIM_SSMPINGD_REPLY; + + /* unicast reply */ + ssmpingd_sendto(ss, buf, len, from); + + /* multicast reply */ + from.sin_addr = qpim_ssmpingd_group_addr; + ssmpingd_sendto(ss, buf, len, from); + + return 0; +} + +static int ssmpingd_sock_read(struct thread *t) +{ + struct ssmpingd_sock *ss; + int sock_fd; + int result; + + zassert(t); + + ss = THREAD_ARG(t); + zassert(ss); + + sock_fd = THREAD_FD(t); + zassert(sock_fd == ss->sock_fd); + + result = ssmpingd_read_msg(ss); + + /* Keep reading */ + ss->t_sock_read = 0; + ssmpingd_read_on(ss); + + return result; +} + +static void ssmpingd_read_on(struct ssmpingd_sock *ss) +{ + zassert(!ss->t_sock_read); + THREAD_READ_ON(master, ss->t_sock_read, + ssmpingd_sock_read, ss, ss->sock_fd); +} + +static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) +{ + struct ssmpingd_sock *ss; + int sock_fd; + + if (!qpim_ssmpingd_list) { + qpim_ssmpingd_list = list_new(); + if (!qpim_ssmpingd_list) { + zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free; + } + + sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64); + if (sock_fd < 0) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: ssmpingd_socket() failure for source %s", + __PRETTY_FUNCTION__, source_str); + return 0; + } + + ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss)); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s", + __PRETTY_FUNCTION__, + sizeof(*ss), source_str); + close(sock_fd); + return 0; + } + + ss->sock_fd = sock_fd; + ss->t_sock_read = 0; + ss->source_addr = source_addr; + ss->creation = pim_time_monotonic_sec(); + ss->requests = 0; + + listnode_add(qpim_ssmpingd_list, ss); + + ssmpingd_read_on(ss); + + return ss; +} + +int pim_ssmpingd_start(struct in_addr source_addr) +{ + struct ssmpingd_sock *ss; + + ss = ssmpingd_find(source_addr); + if (ss) { + /* silently ignore request to recreate entry */ + return 0; + } + + { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_info("%s: starting ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + } + + ss = ssmpingd_new(source_addr); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: ssmpingd_new() failure for source %s", + __PRETTY_FUNCTION__, source_str); + return -1; + } + + return 0; +} + +int pim_ssmpingd_stop(struct in_addr source_addr) +{ + struct ssmpingd_sock *ss; + + ss = ssmpingd_find(source_addr); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: could not find ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + return -1; + } + + { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_info("%s: stopping ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + } + + ssmpingd_delete(ss); + + return 0; +} diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h new file mode 100644 index 00000000..4bef20b2 --- /dev/null +++ b/pimd/pim_ssmpingd.h @@ -0,0 +1,45 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SSMPINGD_H +#define PIM_SSMPINGD_H + +#include + +#include "if.h" + +#include "pim_iface.h" + +struct ssmpingd_sock { + int sock_fd; /* socket */ + struct thread *t_sock_read; /* thread for reading socket */ + struct in_addr source_addr; /* source address */ + int64_t creation; /* timestamp of socket creation */ + int64_t requests; /* counter */ +}; + +void pim_ssmpingd_init(void); +void pim_ssmpingd_destroy(void); +int pim_ssmpingd_start(struct in_addr source_addr); +int pim_ssmpingd_stop(struct in_addr source_addr); + +#endif /* PIM_SSMPINGD_H */ diff --git a/pimd/pim_str.c b/pimd/pim_str.c new file mode 100644 index 00000000..af5a184d --- /dev/null +++ b/pimd/pim_str.c @@ -0,0 +1,46 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include + +#include + +#include "log.h" + +#include "pim_str.h" + +void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) +{ + int save_errno = errno; + + if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { + int e = errno; + zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", + buf_size, e, safe_strerror(e)); + if (onfail) + snprintf(buf, buf_size, "%s", onfail); + } + + errno = save_errno; +} diff --git a/pimd/pim_str.h b/pimd/pim_str.h new file mode 100644 index 00000000..925f17f7 --- /dev/null +++ b/pimd/pim_str.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_STR_H +#define PIM_STR_H + +#include +#include +#include + +void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); + +#endif diff --git a/pimd/pim_time.c b/pimd/pim_time.c new file mode 100644 index 00000000..097b470b --- /dev/null +++ b/pimd/pim_time.c @@ -0,0 +1,165 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include + +#include +#include "log.h" +#include "thread.h" + +#include "pim_time.h" + +static int gettime_monotonic(struct timeval *tv) +{ + int result; + + result = gettimeofday(tv, 0); + if (result) { + zlog_err("%s: gettimeofday() failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, safe_strerror(errno)); + } + + return result; +} + +/* + pim_time_monotonic_sec(): + number of seconds since some unspecified starting point +*/ +int64_t pim_time_monotonic_sec() +{ + struct timeval now_tv; + + if (gettime_monotonic(&now_tv)) { + zlog_err("%s: gettime_monotonic() failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, safe_strerror(errno)); + return -1; + } + + return now_tv.tv_sec; +} + +/* + pim_time_monotonic_dsec(): + number of deciseconds since some unspecified starting point +*/ +int64_t pim_time_monotonic_dsec() +{ + struct timeval now_tv; + int64_t now_dsec; + + if (gettime_monotonic(&now_tv)) { + zlog_err("%s: gettime_monotonic() failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, safe_strerror(errno)); + return -1; + } + + now_dsec = ((int64_t) now_tv.tv_sec) * 10 + ((int64_t) now_tv.tv_usec) / 100000; + + return now_dsec; +} + +int pim_time_mmss(char *buf, int buf_size, long sec) +{ + long mm; + int wr; + + zassert(buf_size >= 5); + + mm = sec / 60; + sec %= 60; + + wr = snprintf(buf, buf_size, "%02ld:%02ld", mm, sec); + + return wr != 8; +} + +static int pim_time_hhmmss(char *buf, int buf_size, long sec) +{ + long hh; + long mm; + int wr; + + zassert(buf_size >= 8); + + hh = sec / 3600; + sec %= 3600; + mm = sec / 60; + sec %= 60; + + wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec); + + return wr != 8; +} + +void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer) +{ + if (t_timer) { + pim_time_mmss(buf, buf_size, + thread_timer_remain_second(t_timer)); + } + else { + snprintf(buf, buf_size, "--:--"); + } +} + +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer) +{ + if (t_timer) { + pim_time_hhmmss(buf, buf_size, + thread_timer_remain_second(t_timer)); + } + else { + snprintf(buf, buf_size, "--:--:--"); + } +} + +void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec) +{ + zassert(buf_size >= 8); + + pim_time_hhmmss(buf, buf_size, uptime_sec); +} + +void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin) +{ + if (begin > 0) + pim_time_uptime(buf, buf_size, now - begin); + else + snprintf(buf, buf_size, "--:--:--"); +} + +long pim_time_timer_remain_msec(struct thread *t_timer) +{ + /* FIXME: Actually fetch msec resolution from thread */ + + /* no timer thread running means timer has expired: return 0 */ + + return t_timer ? + 1000 * thread_timer_remain_second(t_timer) : + 0; +} diff --git a/pimd/pim_time.h b/pimd/pim_time.h new file mode 100644 index 00000000..2984d9a8 --- /dev/null +++ b/pimd/pim_time.h @@ -0,0 +1,40 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_TIME_H +#define PIM_TIME_H + +#include + +#include +#include "thread.h" + +int64_t pim_time_monotonic_sec(void); +int64_t pim_time_monotonic_dsec(void); +int pim_time_mmss(char *buf, int buf_size, long sec); +void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t); +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t); +void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec); +void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin); +long pim_time_timer_remain_msec(struct thread *t_timer); + +#endif /* PIM_TIME_H */ diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c new file mode 100644 index 00000000..95ee5ab0 --- /dev/null +++ b/pimd/pim_tlv.c @@ -0,0 +1,721 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_int.h" +#include "pim_tlv.h" +#include "pim_str.h" +#include "pim_msg.h" + +uint8_t *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value) +{ + uint16_t option_len = 2; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%zd needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + *(uint16_t *) buf = htons(option_value); + buf += option_len; + + return buf; +} + +uint8_t *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2) +{ + uint16_t option_len = 4; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%zd needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + *(uint16_t *) buf = htons(option_value1); + buf += 2; + *(uint16_t *) buf = htons(option_value2); + buf += 2; + + return buf; +} + +uint8_t *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint32_t option_value) +{ + uint16_t option_len = 4; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%zd needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + pim_write_uint32(buf, option_value); + buf += option_len; + + return buf; +} + +#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) + +uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, + struct list *ifconnected) +{ + struct listnode *node; + uint16_t option_len = 0; + + uint8_t *curr; + + node = listhead(ifconnected); + + /* Empty address list ? */ + if (!node) { + return buf; + } + + /* Skip first address (primary) */ + node = listnextnode(node); + + /* Scan secondary address list */ + curr = buf + 4; /* skip T and L */ + for (; node; node = listnextnode(node)) { + struct connected *ifc = listgetdata(node); + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if ((curr + ucast_ipv4_encoding_len) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%zd needed=%zu", + __PRETTY_FUNCTION__, + buf_pastend - curr, ucast_ipv4_encoding_len); + return 0; + } + + /* Write encoded unicast IPv4 address */ + *(uint8_t *) curr = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + ++curr; + *(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */ + ++curr; + memcpy(curr, &p->u.prefix4, sizeof(struct in_addr)); + curr += sizeof(struct in_addr); + + option_len += ucast_ipv4_encoding_len; + } + + if (PIM_DEBUG_PIM_TRACE) { + zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %zu", + __PRETTY_FUNCTION__, + option_len / ucast_ipv4_encoding_len); + } + + if (option_len < 1) { + /* Empty secondary unicast IPv4 address list */ + return buf; + } + + /* + * Write T and L + */ + *(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST); + *(uint16_t *) (buf + 2) = htons(option_len); + + return curr; +} + +static int check_tlv_length(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int correct_len, int option_len) +{ + if (option_len != correct_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s", + label, tlv_name, + option_len, correct_len, + src_str, ifname); + return -1; + } + + return 0; +} + +static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint16_t new, uint16_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint32_t new, uint32_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint32_t new, uint32_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_holdtime, + uint16_t option_len, + const uint8_t *tlv_curr) +{ + const char *label = "holdtime"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint16_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_HOLDTIME, + PIM_TLV_GET_HOLDTIME(tlv_curr), + *hello_option_holdtime); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME); + + *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr); + + return 0; +} + +int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_propagation_delay, + uint16_t *hello_option_override_interval, + uint16_t option_len, + const uint8_t *tlv_curr) +{ + if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay", + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay", + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY, + PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr), + *hello_option_propagation_delay); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY); + + *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr); + if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) { + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); + } + else { + PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); + } + ++tlv_curr; + ++tlv_curr; + *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr); + + return 0; +} + +int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_dr_priority, + uint16_t option_len, + const uint8_t *tlv_curr) +{ + const char *label = "dr_priority"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_DR_PRIORITY, + PIM_TLV_GET_DR_PRIORITY(tlv_curr), + *hello_option_dr_priority); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY); + + *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr); + + return 0; +} + +int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_generation_id, + uint16_t option_len, + const uint8_t *tlv_curr) +{ + const char *label = "generation_id"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_GENERATION_ID, + PIM_TLV_GET_GENERATION_ID(tlv_curr), + *hello_option_generation_id); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID); + + *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr); + + return 0; +} + +int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const uint8_t *buf, + int buf_size) +{ + const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */ + const uint8_t *addr; + const uint8_t *pastend; + int family; + int type; + + if (buf_size < ucast_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, ucast_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *addr++; + type = *addr++; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown unicast address encoding type=%d from %s on %s", + __PRETTY_FUNCTION__, + type, src_str, ifname); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); + + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown unicast address encoding family=%d from %s on %s", + __PRETTY_FUNCTION__, + family, src_str, ifname); + return -4; + } + } + + return addr - buf; +} + +int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const uint8_t *buf, + int buf_size) +{ + const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ + const uint8_t *addr; + const uint8_t *pastend; + int family; + int type; + int mask_len; + + if (buf_size < grp_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: group address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, grp_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *addr++; + type = *addr++; + //++addr; + ++addr; /* skip b_reserved_z fields */ + mask_len = *addr++; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown group address encoding type=%d from %s on %s", + __PRETTY_FUNCTION__, + type, src_str, ifname); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); + p->prefixlen = mask_len; + + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown group address encoding family=%d from %s on %s", + __PRETTY_FUNCTION__, + family, src_str, ifname); + return -4; + } + } + + return addr - buf; +} + +int pim_parse_addr_source(const char *ifname, + struct in_addr src_addr, + struct prefix *p, + uint8_t *flags, + const uint8_t *buf, + int buf_size) +{ + const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ + const uint8_t *addr; + const uint8_t *pastend; + int family; + int type; + int mask_len; + + if (buf_size < src_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: source address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, src_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *addr++; + type = *addr++; + *flags = *addr++; + mask_len = *addr++; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown source address encoding type=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x", + __PRETTY_FUNCTION__, + type, src_str, ifname, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); + p->prefixlen = mask_len; + + /* + RFC 4601: 4.9.1 Encoded Source and Group Address Formats + + Encoded-Source Address + + The mask length MUST be equal to the mask length in bits for + the given Address Family and Encoding Type (32 for IPv4 native + and 128 for IPv6 native). A router SHOULD ignore any messages + received with any other mask length. + */ + if (p->prefixlen != 32) { + char src_str[100]; + pim_inet4_dump("", p->u.prefix4, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 bad source address mask: %s/%d", + __PRETTY_FUNCTION__, src_str, p->prefixlen); + return -4; + } + + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown source address encoding family=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x", + __PRETTY_FUNCTION__, + family, src_str, ifname, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + return -5; + } + } + + return addr - buf; +} + +#define FREE_ADDR_LIST(hello_option_addr_list) \ +{ \ + if (hello_option_addr_list) { \ + list_delete(hello_option_addr_list); \ + hello_option_addr_list = 0; \ + } \ +} + +int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + struct list **hello_option_addr_list, + uint16_t option_len, + const uint8_t *tlv_curr) +{ + const uint8_t *addr; + const uint8_t *pastend; + + zassert(hello_option_addr_list); + + /* + Scan addr list + */ + addr = tlv_curr; + pastend = tlv_curr + option_len; + while (addr < pastend) { + struct prefix tmp; + int addr_offset; + + /* + Parse ucast addr + */ + addr_offset = pim_parse_addr_ucast(ifname, src_addr, &tmp, + addr, pastend - addr); + if (addr_offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifname); + FREE_ADDR_LIST(*hello_option_addr_list); + return -1; + } + addr += addr_offset; + + /* + Debug + */ + if (PIM_DEBUG_PIM_TRACE) { + switch (tmp.family) { + case AF_INET: + { + char addr_str[100]; + char src_str[100]; + pim_inet4_dump("", tmp.u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s", + __PRETTY_FUNCTION__, + *hello_option_addr_list ? + ((int) listcount(*hello_option_addr_list)) : -1, + addr_str, src_str, ifname); + } + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s", + __PRETTY_FUNCTION__, + *hello_option_addr_list ? + ((int) listcount(*hello_option_addr_list)) : -1, + src_str, ifname); + } + } + } + + /* + Exclude neighbor's primary address if incorrectly included in + the secondary address list + */ + if (tmp.family == AF_INET) { + if (tmp.u.prefix4.s_addr == src_addr.s_addr) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring primary address in secondary list from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifname); + continue; + } + } + + /* + Allocate list if needed + */ + if (!*hello_option_addr_list) { + *hello_option_addr_list = list_new(); + if (!*hello_option_addr_list) { + zlog_err("%s %s: failure: hello_option_addr_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return -2; + } + (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free; + } + + /* + Attach addr to list + */ + { + struct prefix *p; + p = prefix_new(); + if (!p) { + zlog_err("%s %s: failure: prefix_new()", + __FILE__, __PRETTY_FUNCTION__); + FREE_ADDR_LIST(*hello_option_addr_list); + return -3; + } + p->family = tmp.family; + p->u.prefix4 = tmp.u.prefix4; + listnode_add(*hello_option_addr_list, p); + } + + } /* while (addr < pastend) */ + + /* + Mark hello option + */ + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST); + + return 0; +} diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h new file mode 100644 index 00000000..b802cf89 --- /dev/null +++ b/pimd/pim_tlv.h @@ -0,0 +1,133 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_TLV_H +#define PIM_TLV_H + +#include + +#include "config.h" +#include "if.h" +#include "linklist.h" + +#ifdef HAVE_INTTYPES_H +#include +#endif /* HAVE_INTTYPES_H */ + +#define PIM_MSG_OPTION_TYPE_HOLDTIME (1) +#define PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY (2) +#define PIM_MSG_OPTION_TYPE_DR_PRIORITY (19) +#define PIM_MSG_OPTION_TYPE_GENERATION_ID (20) +#define PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH (21) +#define PIM_MSG_OPTION_TYPE_ADDRESS_LIST (24) + +typedef uint32_t pim_hello_options; +#define PIM_OPTION_MASK_HOLDTIME (1 << 0) /* recv holdtime */ +#define PIM_OPTION_MASK_LAN_PRUNE_DELAY (1 << 1) /* recv lan_prune_delay */ +#define PIM_OPTION_MASK_DR_PRIORITY (1 << 2) /* recv dr_priority */ +#define PIM_OPTION_MASK_GENERATION_ID (1 << 3) /* recv generation_id */ +#define PIM_OPTION_MASK_ADDRESS_LIST (1 << 4) /* recv secondary address list */ +#define PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION (1 << 5) /* T bit value (valid if recv lan_prune_delay) */ + +#define PIM_RPT_BIT_MASK (1 << 0) +#define PIM_WILDCARD_BIT_MASK (1 << 1) + +#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask)) +#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask)) +#define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask)) + +#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf)) +#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf)) +#define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_PROPAGATION_DELAY(buf) (PIM_TLV_GET_UINT16(buf) & 0x7FFF) +#define PIM_TLV_GET_OVERRIDE_INTERVAL(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(buf) ((*(const uint8_t *)(buf)) & 0x80) +#define PIM_TLV_GET_DR_PRIORITY(buf) PIM_TLV_GET_UINT32(buf) +#define PIM_TLV_GET_GENERATION_ID(buf) PIM_TLV_GET_UINT32(buf) + +#define PIM_TLV_TYPE_SIZE (2) +#define PIM_TLV_LENGTH_SIZE (2) +#define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE) +#define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len)) + +uint8_t *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value); +uint8_t *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2); +uint8_t *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint32_t option_value); +uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, + struct list *ifconnected); + +int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_holdtime, + uint16_t option_len, + const uint8_t *tlv_curr); +int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_propagation_delay, + uint16_t *hello_option_override_interval, + uint16_t option_len, + const uint8_t *tlv_curr); +int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_dr_priority, + uint16_t option_len, + const uint8_t *tlv_curr); +int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_generation_id, + uint16_t option_len, + const uint8_t *tlv_curr); +int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + struct list **hello_option_addr_list, + uint16_t option_len, + const uint8_t *tlv_curr); + +int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const uint8_t *buf, + int buf_size); +int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const uint8_t *buf, + int buf_size); +int pim_parse_addr_source(const char *ifname, + struct in_addr src_addr, + struct prefix *p, + uint8_t *flags, + const uint8_t *buf, + int buf_size); + +#endif /* PIM_TLV_H */ diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c new file mode 100644 index 00000000..d02f9154 --- /dev/null +++ b/pimd/pim_upstream.c @@ -0,0 +1,683 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "zebra/rib.h" + +#include "log.h" +#include "zclient.h" +#include "memory.h" +#include "thread.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_join.h" +#include "pim_zlookup.h" +#include "pim_upstream.h" +#include "pim_ifchannel.h" +#include "pim_neighbor.h" +#include "pim_rpf.h" +#include "pim_zebra.h" +#include "pim_oil.h" +#include "pim_macro.h" + +static void join_timer_start(struct pim_upstream *up); +static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up); + +void pim_upstream_free(struct pim_upstream *up) +{ + XFREE(MTYPE_PIM_UPSTREAM, up); +} + +static void upstream_channel_oil_detach(struct pim_upstream *up) +{ + if (up->channel_oil) { + pim_channel_oil_del(up->channel_oil); + up->channel_oil = 0; + } +} + +void pim_upstream_delete(struct pim_upstream *up) +{ + THREAD_OFF(up->t_join_timer); + + upstream_channel_oil_detach(up); + + /* + notice that listnode_delete() can't be moved + into pim_upstream_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(qpim_upstream_list, up); + + pim_upstream_free(up); +} + +static void send_join(struct pim_upstream *up) +{ + zassert(up->join_state == PIM_UPSTREAM_JOINED); + + + if (PIM_DEBUG_PIM_TRACE) { + if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s", + __PRETTY_FUNCTION__, + src_str, grp_str, rpf_str); + /* warning only */ + } + } + + /* send Join(S,G) to the current upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 1 /* join */); +} + +static int on_join_timer(struct thread *t) +{ + struct pim_upstream *up; + + zassert(t); + up = THREAD_ARG(t); + zassert(up); + + send_join(up); + + up->t_join_timer = 0; + join_timer_start(up); + + return 0; +} + +static void join_timer_start(struct pim_upstream *up) +{ + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + qpim_t_periodic, + src_str, grp_str); + } + + zassert(!up->t_join_timer); + + THREAD_TIMER_ON(master, up->t_join_timer, + on_join_timer, + up, qpim_t_periodic); +} + +void pim_upstream_join_timer_restart(struct pim_upstream *up) +{ + THREAD_OFF(up->t_join_timer); + join_timer_start(up); +} + +static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, + int interval_msec) +{ + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + interval_msec, + src_str, grp_str); + } + + THREAD_OFF(up->t_join_timer); + THREAD_TIMER_MSEC_ON(master, up->t_join_timer, + on_join_timer, + up, interval_msec); +} + +void pim_upstream_join_suppress(struct pim_upstream *up, + struct in_addr rpf_addr, + int holdtime) +{ + long t_joinsuppress_msec; + long join_timer_remain_msec; + + t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface), + 1000 * holdtime); + + join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + rpf_str, + join_timer_remain_msec, t_joinsuppress_msec); + } + + if (join_timer_remain_msec < t_joinsuppress_msec) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, t_joinsuppress_msec); + } + + pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec); + } +} + +void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, + struct pim_upstream *up, + struct in_addr rpf_addr) +{ + long join_timer_remain_msec; + int t_override_msec; + + join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); + t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec", + debug_label, + src_str, grp_str, rpf_str, + join_timer_remain_msec, t_override_msec); + } + + if (join_timer_remain_msec > t_override_msec) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec", + debug_label, + src_str, grp_str, + t_override_msec); + } + + pim_upstream_join_timer_restart_msec(up, t_override_msec); + } +} + +static void forward_on(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + if (pim_macro_chisin_oiflist(ch)) + pim_forward_start(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void forward_off(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_forward_stop(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void pim_upstream_switch(struct pim_upstream *up, + enum pim_upstream_state new_state) +{ + enum pim_upstream_state old_state = up->join_state; + + zassert(old_state != new_state); + + up->join_state = new_state; + up->state_transition = pim_time_monotonic_sec(); + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"), + src_str, grp_str); + } + + pim_upstream_update_assert_tracking_desired(up); + + if (new_state == PIM_UPSTREAM_JOINED) { + forward_on(up); + send_join(up); + join_timer_start(up); + } + else { + forward_off(up); + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 0 /* prune */); + zassert(up->t_join_timer); + THREAD_OFF(up->t_join_timer); + } + +} + +static struct pim_upstream *pim_upstream_new(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_upstream *up; + + up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + if (!up) { + zlog_err("%s: PIM XMALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*up)); + return 0; + } + + up->source_addr = source_addr; + up->group_addr = group_addr; + up->flags = 0; + up->ref_count = 1; + up->t_join_timer = 0; + up->join_state = 0; + up->state_transition = pim_time_monotonic_sec(); + up->channel_oil = 0; + + up->rpf.source_nexthop.interface = 0; + up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference; + up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric; + up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY; + + pim_rpf_update(up, 0); + + listnode_add(qpim_upstream_list, up); + + return up; +} + +struct pim_upstream *pim_upstream_find(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct listnode *up_node; + struct pim_upstream *up; + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { + if ( + (source_addr.s_addr == up->source_addr.s_addr) && + (group_addr.s_addr == up->group_addr.s_addr) + ) { + return up; + } + } + + return 0; +} + +struct pim_upstream *pim_upstream_add(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_upstream *up; + + up = pim_upstream_find(source_addr, group_addr); + if (up) { + ++up->ref_count; + } + else { + up = pim_upstream_new(source_addr, group_addr); + } + + return up; +} + +void pim_upstream_del(struct pim_upstream *up) +{ + --up->ref_count; + + if (up->ref_count < 1) { + pim_upstream_delete(up); + } +} + +/* + Evaluate JoinDesired(S,G): + + JoinDesired(S,G) is true if there is a downstream (S,G) interface I + in the set: + + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + + JoinDesired(S,G) may be affected by changes in the following: + + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface + + See also pim_upstream_update_join_desired() below. + */ +int pim_upstream_evaluate_join_desired(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; + + if (pim_macro_ch_lost_assert(ch)) + continue; /* keep searching */ + + if (pim_macro_chisin_joins_or_include(ch)) + return 1; /* true */ + } /* scan iface channel list */ + } /* scan iflist */ + + return 0; /* false */ +} + +/* + See also pim_upstream_evaluate_join_desired() above. +*/ +void pim_upstream_update_join_desired(struct pim_upstream *up) +{ + int was_join_desired; /* boolean */ + int is_join_desired; /* boolean */ + + was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags); + + is_join_desired = pim_upstream_evaluate_join_desired(up); + if (is_join_desired) + PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags); + else + PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags); + + /* switched from false to true */ + if (is_join_desired && !was_join_desired) { + zassert(up->join_state == PIM_UPSTREAM_NOTJOINED); + pim_upstream_switch(up, PIM_UPSTREAM_JOINED); + return; + } + + /* switched from true to false */ + if (!is_join_desired && was_join_desired) { + zassert(up->join_state == PIM_UPSTREAM_JOINED); + pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED); + return; + } +} + +/* + RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages + Transitions from Joined State + RPF'(S,G) GenID changes + + The upstream (S,G) state machine remains in Joined state. If the + Join Timer is set to expire in more than t_override seconds, reset + it so that it expires after t_override seconds. +*/ +void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr) +{ + struct listnode *up_node; + struct listnode *up_nextnode; + struct pim_upstream *up; + + /* + Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr + */ + for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + + if (PIM_DEBUG_PIM_TRACE) { + char neigh_str[100]; + char src_str[100]; + char grp_str[100]; + char rpf_addr_str[100]; + pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s", + __PRETTY_FUNCTION__, + neigh_str, src_str, grp_str, + up->join_state == PIM_UPSTREAM_JOINED, + rpf_addr_str); + } + + /* consider only (S,G) upstream in Joined state */ + if (up->join_state != PIM_UPSTREAM_JOINED) + continue; + + /* match RPF'(S,G)=neigh_addr */ + if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr) + continue; + + pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change", + up, neigh_addr); + } +} + + +void pim_upstream_rpf_interface_changed(struct pim_upstream *up, + struct interface *old_rpf_ifp) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* search all ifchannels */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + if ( + /* RPF_interface(S) was NOT I */ + (old_rpf_ifp == ch->interface) + && + /* RPF_interface(S) stopped being I */ + (ch->upstream->rpf.source_nexthop.interface != ch->interface) + ) { + assert_action_a5(ch); + } + } /* PIM_IFASSERT_I_AM_LOSER */ + + pim_ifchannel_update_assert_tracking_desired(ch); + } + } +} + +void pim_upstream_update_could_assert(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_could_assert(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +void pim_upstream_update_my_assert_metric(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_my_assert_metric(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_assert_tracking_desired(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h new file mode 100644 index 00000000..5b5182dd --- /dev/null +++ b/pimd/pim_upstream.h @@ -0,0 +1,122 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_UPSTREAM_H +#define PIM_UPSTREAM_H + +#include + +#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0) +#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0) + +#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +/* + RFC 4601: + + Metric Preference + Preference value assigned to the unicast routing protocol that + provided the route to the multicast source or Rendezvous-Point. + + Metric + The unicast routing table metric associated with the route used to + reach the multicast source or Rendezvous-Point. The metric is in + units applicable to the unicast routing protocol used. +*/ +struct pim_nexthop { + struct interface *interface; /* RPF_interface(S) */ + struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */ + uint32_t mrib_metric_preference; /* MRIB.pref(S) */ + uint32_t mrib_route_metric; /* MRIB.metric(S) */ +}; + +struct pim_rpf { + struct pim_nexthop source_nexthop; + struct in_addr rpf_addr; /* RPF'(S,G) */ +}; + +enum pim_rpf_result { + PIM_RPF_OK = 0, + PIM_RPF_CHANGED, + PIM_RPF_FAILURE +}; + +enum pim_upstream_state { + PIM_UPSTREAM_NOTJOINED, + PIM_UPSTREAM_JOINED +}; + +/* + Upstream (S,G) channel in Joined state + + (S,G) in the "Not Joined" state is not represented + + See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message +*/ +struct pim_upstream { + struct in_addr source_addr; /* (S,G) source key */ + struct in_addr group_addr; /* (S,G) group key */ + uint32_t flags; + struct channel_oil *channel_oil; + + enum pim_upstream_state join_state; + int ref_count; + + struct pim_rpf rpf; + + struct thread *t_join_timer; + int64_t state_transition; /* Record current state uptime */ +}; + +void pim_upstream_free(struct pim_upstream *up); +void pim_upstream_delete(struct pim_upstream *up); +struct pim_upstream *pim_upstream_find(struct in_addr source_addr, + struct in_addr group_addr); +struct pim_upstream *pim_upstream_add(struct in_addr source_addr, + struct in_addr group_addr); +void pim_upstream_del(struct pim_upstream *up); + +int pim_upstream_evaluate_join_desired(struct pim_upstream *up); +void pim_upstream_update_join_desired(struct pim_upstream *up); + +void pim_upstream_join_suppress(struct pim_upstream *up, + struct in_addr rpf_addr, + int holdtime); +void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, + struct pim_upstream *up, + struct in_addr rpf_addr); +void pim_upstream_join_timer_restart(struct pim_upstream *up); +void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr); +void pim_upstream_rpf_interface_changed(struct pim_upstream *up, + struct interface *old_rpf_ifp); + +void pim_upstream_update_could_assert(struct pim_upstream *up); +void pim_upstream_update_my_assert_metric(struct pim_upstream *up); + +#endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c new file mode 100644 index 00000000..fdfed2bf --- /dev/null +++ b/pimd/pim_util.c @@ -0,0 +1,122 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" + +#include "pim_util.h" + +/* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + If QQIC < 128, QQI = QQIC + If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3) + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |1| exp | mant | + +-+-+-+-+-+-+-+-+ + + Since exp=0..7 then (exp+3)=3..10, then QQI has + one of the following bit patterns: + + exp=0: QQI = 0000.0000.1MMM.M000 + exp=1: QQI = 0000.0001.MMMM.0000 + ... + exp=6: QQI = 001M.MMM0.0000.0000 + exp=7: QQI = 01MM.MM00.0000.0000 + --------- --------- + 0x4 0x0 0x0 0x0 +*/ +uint8_t igmp_msg_encode16to8(uint16_t value) +{ + uint8_t code; + + if (value < 128) { + code = value; + } + else { + uint16_t mask = 0x4000; + uint8_t exp; + uint16_t mant; + for (exp = 7; exp > 0; --exp) { + if (mask & value) + break; + mask >>= 1; + } + mant = 0x000F & (value >> (exp + 3)); + code = ((uint8_t) 1 << 7) | ((uint8_t) exp << 4) | (uint8_t) mant; + } + + return code; +} + +/* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + If QQIC < 128, QQI = QQIC + If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3) + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |1| exp | mant | + +-+-+-+-+-+-+-+-+ +*/ +uint16_t igmp_msg_decode8to16(uint8_t code) +{ + uint16_t value; + + if (code < 128) { + value = code; + } + else { + uint16_t mant = (code & 0x0F); + uint8_t exp = (code & 0x70) >> 4; + value = (mant | 0x10) << (exp + 3); + } + + return value; +} + +void pim_pkt_dump(const char *label, const uint8_t *buf, int size) +{ + char dump_buf[1000]; + int i = 0; + int j = 0; + + for (; i < size; ++i, j += 2) { + int left = sizeof(dump_buf) - j; + if (left < 4) { + if (left > 1) { + strcat(dump_buf + j, "!"); /* mark as truncated */ + } + break; + } + snprintf(dump_buf + j, left, "%02x", buf[i]); + } + + zlog_debug("%s: pkt dump size=%d: %s", + label, + size, + dump_buf); +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h new file mode 100644 index 00000000..a8613e2b --- /dev/null +++ b/pimd/pim_util.h @@ -0,0 +1,37 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_UTIL_H +#define PIM_UTIL_H + +#include + +#include + +#include "checksum.h" + +uint8_t igmp_msg_encode16to8(uint16_t value); +uint16_t igmp_msg_decode8to16(uint8_t code); + +void pim_pkt_dump(const char *label, const uint8_t *buf, int size); + +#endif /* PIM_UTIL_H */ diff --git a/pimd/pim_version.c b/pimd/pim_version.c new file mode 100644 index 00000000..fe7e5634 --- /dev/null +++ b/pimd/pim_version.c @@ -0,0 +1,25 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_version.h" + +const char * const PIMD_VERSION = PIMD_VERSION_STR; diff --git a/pimd/pim_version.h b/pimd/pim_version.h new file mode 100644 index 00000000..ef9f370c --- /dev/null +++ b/pimd/pim_version.h @@ -0,0 +1,30 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_VERSION_H +#define PIM_VERSION_H + +#define PIMD_VERSION_STR "0.166" + +const char * const PIMD_VERSION; + +#endif /* PIM_VERSION_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c new file mode 100644 index 00000000..bcace95c --- /dev/null +++ b/pimd/pim_vty.c @@ -0,0 +1,178 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "if.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_vty.h" +#include "pim_iface.h" +#include "pim_cmd.h" +#include "pim_str.h" +#include "pim_ssmpingd.h" + +int pim_debug_config_write(struct vty *vty) +{ + int writes = 0; + + if (PIM_DEBUG_IGMP_EVENTS) { + vty_out(vty, "debug igmp events%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_IGMP_PACKETS) { + vty_out(vty, "debug igmp packets%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_IGMP_TRACE) { + vty_out(vty, "debug igmp trace%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_MROUTE) { + vty_out(vty, "debug mroute%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_PIM_EVENTS) { + vty_out(vty, "debug pim events%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_PACKETS) { + vty_out(vty, "debug pim packets%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_PACKETDUMP_SEND) { + vty_out(vty, "debug pim packet-dump send%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_TRACE) { + vty_out(vty, "debug pim trace%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_ZEBRA) { + vty_out(vty, "debug pim zebra%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_SSMPINGD) { + vty_out(vty, "debug ssmpingd%s", VTY_NEWLINE); + ++writes; + } + + return writes; +} + +int pim_global_config_write(struct vty *vty) +{ + int writes = 0; + + if (PIM_MROUTE_IS_ENABLED) { + vty_out(vty, "%s%s", PIM_CMD_IP_MULTICAST_ROUTING, VTY_NEWLINE); + ++writes; + } + + if (qpim_ssmpingd_list) { + struct listnode *node; + struct ssmpingd_sock *ss; + vty_out(vty, "!%s", VTY_NEWLINE); + ++writes; + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE); + ++writes; + } + } + + return writes; +} + +int pim_interface_config_write(struct vty *vty) +{ + int writes = 0; + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + + /* IF name */ + vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE); + ++writes; + + if (ifp->info) { + struct pim_interface *pim_ifp = ifp->info; + + /* IF ip pim ssm */ + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + vty_out(vty, " ip pim ssm%s", VTY_NEWLINE); + ++writes; + } + + /* IF ip igmp */ + if (PIM_IF_TEST_IGMP(pim_ifp->options)) { + vty_out(vty, " ip igmp%s", VTY_NEWLINE); + ++writes; + } + + /* IF ip igmp query-interval */ + vty_out(vty, " %s %d%s", + PIM_CMD_IP_IGMP_QUERY_INTERVAL, + pim_ifp->igmp_default_query_interval, + VTY_NEWLINE); + ++writes; + + /* IF ip igmp query-max-response-time */ + vty_out(vty, " %s %d%s", + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, + pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + ++writes; + + /* IF ip igmp join */ + if (pim_ifp->igmp_join_list) { + struct listnode *node; + struct igmp_join *ij; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ij->source_addr, source_str, sizeof(source_str)); + vty_out(vty, " ip igmp join %s %s%s", + group_str, source_str, + VTY_NEWLINE); + ++writes; + } + } + } + vty_out(vty, "!%s", VTY_NEWLINE); + ++writes; + } + + return writes; +} diff --git a/pimd/pim_vty.h b/pimd/pim_vty.h new file mode 100644 index 00000000..904ee553 --- /dev/null +++ b/pimd/pim_vty.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_VTY_H +#define PIM_VTY_H + +#include "vty.h" + +int pim_debug_config_write(struct vty *vty); +int pim_global_config_write(struct vty *vty); +int pim_interface_config_write(struct vty *vty); + +#endif /* PIM_VTY_H */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c new file mode 100644 index 00000000..6f241d59 --- /dev/null +++ b/pimd/pim_zebra.c @@ -0,0 +1,1310 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "zebra/rib.h" + +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_zebra.h" +#include "pim_iface.h" +#include "pim_str.h" +#include "pim_oil.h" +#include "pim_rpf.h" +#include "pim_time.h" +#include "pim_join.h" +#include "pim_zlookup.h" +#include "pim_ifchannel.h" + +#undef PIM_DEBUG_IFADDR_DUMP +#define PIM_DEBUG_IFADDR_DUMP + +static int fib_lookup_if_vif_index(struct in_addr addr); +static int del_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask); + +static void zclient_broken(struct zclient *zclient) +{ + struct listnode *ifnode; + struct interface *ifp; + + zlog_warn("%s %s: broken zclient connection", + __FILE__, __PRETTY_FUNCTION__); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + pim_if_addr_del_all(ifp); + } + + /* upon return, zclient will discard connected addresses */ +} + +/* Router-id update message from zebra. */ +static int pim_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + return 0; +} + +static int pim_zebra_if_add(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api adds/dels interfaces using the same call + interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_add_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (if_is_operative(ifp)) + pim_if_addr_add_all(ifp); + + return 0; +} + +static int pim_zebra_if_del(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api adds/dels interfaces using the same call + interface_add_read below, see comments in lib/zclient.c + + comments in lib/zclient.c seem to indicate that calling + zebra_interface_add_read is the correct call, but that + results in an attemted out of bounds read which causes + pimd to assert. Other clients use zebra_interface_state_read + and it appears to work just fine. + */ + ifp = zebra_interface_state_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) + pim_if_addr_del_all(ifp); + + return 0; +} + +static int pim_zebra_if_state_up(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api notifies interface up/down events by using the same call + zebra_interface_state_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf); + if (!ifp) + return 0; + + zlog_info("INTERFACE UP: %s ifindex=%d", ifp->name, ifp->ifindex); + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (if_is_operative(ifp)) { + /* + pim_if_addr_add_all() suffices for bringing up both IGMP and PIM + */ + pim_if_addr_add_all(ifp); + } + + return 0; +} + +static int pim_zebra_if_state_down(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api notifies interface up/down events by using the same call + zebra_interface_state_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf); + if (!ifp) + return 0; + + zlog_info("INTERFACE DOWN: %s ifindex=%d", ifp->name, ifp->ifindex); + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) { + /* + pim_if_addr_del_all() suffices for shutting down IGMP, + but not for shutting down PIM + */ + pim_if_addr_del_all(ifp); + + /* + pim_sock_delete() closes the socket, stops read and timer threads, + and kills all neighbors. + */ + if (ifp->info) { + pim_sock_delete(ifp, "link down"); + } + } + + return 0; +} + +#ifdef PIM_DEBUG_IFADDR_DUMP +static void dump_if_address(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + + zlog_debug("%s %s: interface %s addresses:", + __FILE__, __PRETTY_FUNCTION__, + ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + zlog_debug("%s %s: interface %s address %s %s", + __FILE__, __PRETTY_FUNCTION__, + ifp->name, + inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); + } +} +#endif + +static int pim_zebra_if_address_add(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct connected *c; + struct prefix *p; + + zassert(command == ZEBRA_INTERFACE_ADDRESS_ADD); + + /* + zebra api notifies address adds/dels events by using the same call + interface_add_read below, see comments in lib/zclient.c + + zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) + will add address to interface list by calling + connected_add_by_prefix() + */ + c = zebra_interface_address_read(command, zclient->ibuf); + if (!c) + return 0; + + p = c->address; + if (p->family != AF_INET) + return 0; + + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: %s connected IP address %s flags %u %s", + __PRETTY_FUNCTION__, + c->ifp->name, buf, c->flags, + CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); + +#ifdef PIM_DEBUG_IFADDR_DUMP + dump_if_address(c->ifp); +#endif + } + + if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) { + /* trying to add primary address */ + + struct in_addr primary_addr = pim_find_primary_addr(c->ifp); + if (primary_addr.s_addr != p->u.prefix4.s_addr) { + /* but we had a primary address already */ + + char buf[BUFSIZ]; + char old[100]; + + prefix2str(p, buf, BUFSIZ); + pim_inet4_dump("", primary_addr, old, sizeof(old)); + + zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s", + __PRETTY_FUNCTION__, + c->ifp->name, old, buf); + SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); + } + } + + pim_if_addr_add(c); + + return 0; +} + +static int pim_zebra_if_address_del(int command, struct zclient *client, + zebra_size_t length) +{ + struct connected *c; + struct prefix *p; + + zassert(command == ZEBRA_INTERFACE_ADDRESS_DELETE); + + /* + zebra api notifies address adds/dels events by using the same call + interface_add_read below, see comments in lib/zclient.c + + zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) + will remove address from interface list by calling + connected_delete_by_prefix() + */ + c = zebra_interface_address_read(command, client->ibuf); + if (!c) + return 0; + + p = c->address; + if (p->family != AF_INET) + return 0; + + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: %s disconnected IP address %s flags %u %s", + __PRETTY_FUNCTION__, + c->ifp->name, buf, c->flags, + CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); + +#ifdef PIM_DEBUG_IFADDR_DUMP + dump_if_address(c->ifp); +#endif + } + + pim_if_addr_del(c, 0); + + return 0; +} + +static void scan_upstream_rpf_cache() +{ + struct listnode *up_node; + struct listnode *up_nextnode; + struct pim_upstream *up; + + for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + struct in_addr old_rpf_addr; + enum pim_rpf_result rpf_result; + + rpf_result = pim_rpf_update(up, &old_rpf_addr); + if (rpf_result == PIM_RPF_FAILURE) + continue; + + if (rpf_result == PIM_RPF_CHANGED) { + + if (up->join_state == PIM_UPSTREAM_JOINED) { + + /* + RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages + + Transitions from Joined State + + RPF'(S,G) changes not due to an Assert + + The upstream (S,G) state machine remains in Joined + state. Send Join(S,G) to the new upstream neighbor, which is + the new value of RPF'(S,G). Send Prune(S,G) to the old + upstream neighbor, which is the old value of RPF'(S,G). Set + the Join Timer (JT) to expire after t_periodic seconds. + */ + + + /* send Prune(S,G) to the old upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + old_rpf_addr, + up->source_addr, + up->group_addr, + 0 /* prune */); + + /* send Join(S,G) to the current upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 1 /* join */); + + pim_upstream_join_timer_restart(up); + } /* up->join_state == PIM_UPSTREAM_JOINED */ + + /* FIXME can join_desired actually be changed by pim_rpf_update() + returning PIM_RPF_CHANGED ? */ + pim_upstream_update_join_desired(up); + + } /* PIM_RPF_CHANGED */ + + } /* for (qpim_upstream_list) */ + +} + +void pim_scan_oil() +{ + struct listnode *node; + struct listnode *nextnode; + struct channel_oil *c_oil; + + qpim_scan_oil_last = pim_time_monotonic_sec(); + ++qpim_scan_oil_events; + + for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) { + int old_vif_index; + int input_iface_vif_index = fib_lookup_if_vif_index(c_oil->oil.mfcc_origin); + if (input_iface_vif_index < 1) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: could not find input interface for (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + continue; + } + + if (input_iface_vif_index == c_oil->oil.mfcc_parent) { + /* RPF unchanged */ + continue; + } + + if (PIM_DEBUG_ZEBRA) { + struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", input_iface_vif_index); + } + + /* new iif loops to existing oif ? */ + if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) { + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + + if (PIM_DEBUG_ZEBRA) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + new_iif ? new_iif->name : "", input_iface_vif_index); + } + + del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY); + } + + /* update iif vif_index */ + old_vif_index = c_oil->oil.mfcc_parent; + c_oil->oil.mfcc_parent = input_iface_vif_index; + + /* update kernel multicast forwarding cache (MFC) */ + if (pim_mroute_add(&c_oil->oil)) { + /* just log warning */ + struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index); + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", input_iface_vif_index); + continue; + } + + } /* for (qpim_channel_oil_list) */ +} + +static int on_rpf_cache_refresh(struct thread *t) +{ + zassert(t); + zassert(qpim_rpf_cache_refresher); + + qpim_rpf_cache_refresher = 0; + + /* update PIM protocol state */ + scan_upstream_rpf_cache(); + + /* update kernel multicast forwarding cache (MFC) */ + pim_scan_oil(); + + qpim_rpf_cache_refresh_last = pim_time_monotonic_sec(); + ++qpim_rpf_cache_refresh_events; + + return 0; +} + +static void sched_rpf_cache_refresh() +{ + ++qpim_rpf_cache_refresh_requests; + + if (qpim_rpf_cache_refresher) { + /* Refresh timer is already running */ + return; + } + + /* Start refresh timer */ + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: triggering %ld msec timer", + __PRETTY_FUNCTION__, + qpim_rpf_cache_refresh_delay_msec); + } + + THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher, + on_rpf_cache_refresh, + 0, qpim_rpf_cache_refresh_delay_msec); +} + +static int redist_read_ipv4_route(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct stream *s; + struct zapi_ipv4 api; + unsigned long ifindex; + struct in_addr nexthop; + struct prefix_ipv4 p; + int min_len = 4; + + if (length < min_len) { + zlog_warn("%s %s: short buffer: length=%d min=%d", + __FILE__, __PRETTY_FUNCTION__, + length, min_len); + return -1; + } + + s = zclient->ibuf; + ifindex = 0; + nexthop.s_addr = 0; + + /* Type, flags, message. */ + api.type = stream_getc(s); + api.flags = stream_getc(s); + api.message = stream_getc(s); + + /* IPv4 prefix length. */ + memset(&p, 0, sizeof(struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = stream_getc(s); + + min_len += + PSIZE(p.prefixlen) + + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s", + __FILE__, __PRETTY_FUNCTION__, + length, min_len, + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); + } + + if (length < min_len) { + zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s", + __FILE__, __PRETTY_FUNCTION__, + length, min_len, + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); + return -1; + } + + /* IPv4 prefix. */ + stream_get(&p.prefix, s, PSIZE(p.prefixlen)); + + /* Nexthop, ifindex, distance, metric. */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { + api.nexthop_num = stream_getc(s); + nexthop.s_addr = stream_get_ipv4(s); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) { + api.ifindex_num = stream_getc(s); + ifindex = stream_getl(s); + } + + api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? + stream_getc(s) : + 0; + + api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? + stream_getl(s) : + 0; + + switch (command) { + case ZEBRA_IPV4_ROUTE_ADD: + if (PIM_DEBUG_ZEBRA) { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("%s: add %s %s/%d " + "nexthop %s ifindex %ld metric%s %u distance%s %u", + __PRETTY_FUNCTION__, + zebra_route_string(api.type), + inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), + p.prefixlen, + inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])), + ifindex, + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss", + api.metric, + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss", + api.distance); + } + break; + case ZEBRA_IPV4_ROUTE_DELETE: + if (PIM_DEBUG_ZEBRA) { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("%s: delete %s %s/%d " + "nexthop %s ifindex %ld metric%s %u distance%s %u", + __PRETTY_FUNCTION__, + zebra_route_string(api.type), + inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), + p.prefixlen, + inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])), + ifindex, + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss", + api.metric, + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss", + api.distance); + } + break; + default: + zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command); + return -1; + } + + sched_rpf_cache_refresh(); + + return 0; +} + +void pim_zebra_init(char *zebra_sock_path) +{ + int i; + + if (zebra_sock_path) + zclient_serv_path_set(zebra_sock_path); + +#ifdef HAVE_TCP_ZEBRA + zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT); +#else + zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", zclient_serv_path_get()); +#endif + + /* Socket for receiving updates from Zebra daemon */ + qpim_zclient_update = zclient_new(); + + qpim_zclient_update->router_id_update = pim_router_id_update_zebra; + qpim_zclient_update->interface_add = pim_zebra_if_add; + qpim_zclient_update->interface_delete = pim_zebra_if_del; + qpim_zclient_update->interface_up = pim_zebra_if_state_up; + qpim_zclient_update->interface_down = pim_zebra_if_state_down; + qpim_zclient_update->interface_address_add = pim_zebra_if_address_add; + qpim_zclient_update->interface_address_delete = pim_zebra_if_address_del; + qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route; + qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; + + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("zclient_init cleared redistribution request"); + } + + zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM); + + /* Request all redistribution */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (i == qpim_zclient_update->redist_default) + continue; + qpim_zclient_update->redist[i] = 1; + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: requesting redistribution for %s (%i)", + __PRETTY_FUNCTION__, zebra_route_string(i), i); + } + } + + /* Request default information */ + qpim_zclient_update->default_information = 1; + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("%s: requesting default information redistribution", + __PRETTY_FUNCTION__); + + zlog_notice("%s: zclient update socket initialized", + __PRETTY_FUNCTION__); + } + + zassert(!qpim_zclient_lookup); + qpim_zclient_lookup = zclient_lookup_new(); + zassert(qpim_zclient_lookup); +} + +void igmp_anysource_forward_start(struct igmp_group *group) +{ + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + zassert(group->group_filtermode_isexcl); + zassert(listcount(group->group_source_list) < 1); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s %s: UNIMPLEMENTED", + __FILE__, __PRETTY_FUNCTION__); + } +} + +void igmp_anysource_forward_stop(struct igmp_group *group) +{ + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + zassert((!group->group_filtermode_isexcl) || (listcount(group->group_source_list) > 0)); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s %s: UNIMPLEMENTED", + __FILE__, __PRETTY_FUNCTION__); + } +} + +static int fib_lookup_if_vif_index(struct in_addr addr) +{ + struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + int num_ifindex; + int vif_index; + int first_ifindex; + + num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr, + PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + return -1; + } + + first_ifindex = nexthop_tab[0].ifindex; + + if (num_ifindex > 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", + __FILE__, __PRETTY_FUNCTION__, + num_ifindex, addr_str, first_ifindex); + /* debug warning only, do not return */ + } + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, ifindex2ifname(first_ifindex), addr_str); + } + + vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex); + + if (vif_index < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s", + __FILE__, __PRETTY_FUNCTION__, + vif_index, addr_str); + return -2; + } + + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + + if (vif_index > qpim_mroute_oif_highest_vif_index) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s", + __FILE__, __PRETTY_FUNCTION__, + vif_index, qpim_mroute_oif_highest_vif_index, addr_str); + + zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?", + __FILE__, __PRETTY_FUNCTION__, + ifindex2ifname(vif_index), + vif_index); + + return -3; + } + + return vif_index; +} + +static int add_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask) +{ + struct pim_interface *pim_ifp; + int old_ttl; + + zassert(channel_oil); + + pim_ifp = oif->info; + + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + + if (pim_ifp->mroute_vif_index < 1) { + zlog_warn("%s %s: interface %s vif_index=%d < 1", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index); + return -1; + } + +#ifdef PIM_ENFORCE_LOOPFREE_MFC + /* + Prevent creating MFC entry with OIF=IIF. + + This is a protection against implementation mistakes. + + PIM protocol implicitely ensures loopfree multicast topology. + + IGMP must be protected against adding looped MFC entries created + by both source and receiver attached to the same interface. See + TODO T22. + */ + if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -2; + } +#endif + + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); + + /* Prevent single protocol from subscribing same interface to + channel (S,G) multiple times */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + return -3; + } + + /* Allow other protocol to request subscription of same interface to + channel (S,G) multiple times, by silently ignoring further + requests */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) { + + /* Check the OIF really exists before returning, and only log + warning otherwise */ + if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + + return 0; + } + + old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]; + + if (old_ttl > 0) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -4; + } + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; + + if (pim_mroute_add(&channel_oil->oil)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl; + return -5; + } + + channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec(); + ++channel_oil->oil_size; + channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; + + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + + return 0; +} + +static int del_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask) +{ + struct pim_interface *pim_ifp; + int old_ttl; + + zassert(channel_oil); + + pim_ifp = oif->info; + + zassert(pim_ifp->mroute_vif_index >= 1); + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); + + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + + /* Prevent single protocol from unsubscribing same interface from + channel (S,G) multiple times */ + if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + return -2; + } + + /* Mark that protocol is no longer interested in this OIF */ + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; + + /* Allow multiple protocols to unsubscribe same interface from + channel (S,G) multiple times, by silently ignoring requests while + there is at least one protocol interested in the channel */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) { + + /* Check the OIF keeps existing before returning, and only log + warning otherwise */ + if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + + return 0; + } + + old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]; + + if (old_ttl < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -3; + } + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + + if (pim_mroute_add(&channel_oil->oil)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl; + return -4; + } + + --channel_oil->oil_size; + + if (channel_oil->oil_size < 1) { + if (pim_mroute_del(&channel_oil->oil)) { + /* just log a warning in case of failure */ + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } + + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + + return 0; +} + +void igmp_source_forward_start(struct igmp_source *source) +{ + struct igmp_group *group; + int result; + + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from installing multicast route multiple + times */ + if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + + if (!source->source_channel_oil) { + struct pim_interface *pim_oif; + int input_iface_vif_index = fib_lookup_if_vif_index(source->source_addr); + if (input_iface_vif_index < 1) { + char source_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + return; + } + + /* + Protect IGMP against adding looped MFC entries created by both + source and receiver attached to the same interface. See TODO + T22. + */ + pim_oif = source->source_group->group_igmp_sock->interface->info; + if (!pim_oif) { + zlog_warn("%s: multicast not enabled on oif=%s ?", + __PRETTY_FUNCTION__, + source->source_group->group_igmp_sock->interface->name); + return; + } + if (pim_oif->mroute_vif_index < 1) { + zlog_warn("%s %s: oif=%s vif_index=%d < 1", + __FILE__, __PRETTY_FUNCTION__, + source->source_group->group_igmp_sock->interface->name, + pim_oif->mroute_vif_index); + return; + } + if (input_iface_vif_index == pim_oif->mroute_vif_index) { + /* ignore request for looped MFC entry */ + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + input_iface_vif_index); + } + return; + } + + source->source_channel_oil = pim_channel_oil_add(group->group_addr, + source->source_addr, + input_iface_vif_index); + if (!source->source_channel_oil) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + return; + } + } + + result = add_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); + if (result) { + zlog_warn("%s: add_oif() failed with return=%d", + __func__, result); + return; + } + + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + pim_ifchannel_local_membership_add(group->group_igmp_sock->interface, + source->source_addr, group->group_addr); + + IGMP_SOURCE_DO_FORWARDING(source->source_flags); +} + +/* + igmp_source_forward_stop: stop fowarding, but keep the source + igmp_source_delete: stop fowarding, and delete the source + */ +void igmp_source_forward_stop(struct igmp_source *source) +{ + struct igmp_group *group; + int result; + + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from removing multicast route multiple + times */ + if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + + /* + It appears that in certain circumstances that + igmp_source_forward_stop is called when IGMP forwarding + was not enabled in oif_flags for this outgoing interface. + Possibly because of multiple calls. When that happens, we + enter the below if statement and this function returns early + which in turn triggers the calling function to assert. + Making the call to del_oif and ignoring the return code + fixes the issue without ill effect, similar to + pim_forward_stop below. + */ + result = del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); + if (result) { + zlog_warn("%s: del_oif() failed with return=%d", + __func__, result); + return; + } + + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + pim_ifchannel_local_membership_del(group->group_igmp_sock->interface, + source->source_addr, group->group_addr); + + IGMP_SOURCE_DONT_FORWARDING(source->source_flags); +} + +void pim_forward_start(struct pim_ifchannel *ch) +{ + struct pim_upstream *up = ch->upstream; + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) oif=%s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + } + + if (!up->channel_oil) { + int input_iface_vif_index = fib_lookup_if_vif_index(up->source_addr); + if (input_iface_vif_index < 1) { + char source_str[100]; + pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + return; + } + + up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr, + input_iface_vif_index); + if (!up->channel_oil) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", up->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + return; + } + } + + add_oif(up->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM); +} + +void pim_forward_stop(struct pim_ifchannel *ch) +{ + struct pim_upstream *up = ch->upstream; + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) oif=%s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + } + + if (!up->channel_oil) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + + return; + } + + del_oif(up->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM); +} diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h new file mode 100644 index 00000000..d624c866 --- /dev/null +++ b/pimd/pim_zebra.h @@ -0,0 +1,42 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ZEBRA_H +#define PIM_ZEBRA_H + +#include "pim_igmp.h" +#include "pim_ifchannel.h" + +void pim_zebra_init(char *zebra_sock_path); + +void pim_scan_oil(void); + +void igmp_anysource_forward_start(struct igmp_group *group); +void igmp_anysource_forward_stop(struct igmp_group *group); + +void igmp_source_forward_start(struct igmp_source *source); +void igmp_source_forward_stop(struct igmp_source *source); + +void pim_forward_start(struct pim_ifchannel *ch); +void pim_forward_stop(struct pim_ifchannel *ch); + +#endif /* PIM_ZEBRA_H */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c new file mode 100644 index 00000000..2e71dc4e --- /dev/null +++ b/pimd/pim_zlookup.c @@ -0,0 +1,457 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "zebra/rib.h" + +#include "log.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" +#include "thread.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_zlookup.h" + +extern int zclient_debug; + +static void zclient_lookup_sched(struct zclient *zlookup, int delay); + +/* Connect to zebra for nexthop lookup. */ +static int zclient_lookup_connect(struct thread *t) +{ + struct zclient *zlookup; + + zlookup = THREAD_ARG(t); + zlookup->t_connect = NULL; + + if (zlookup->sock >= 0) { + return 0; + } + + if (zclient_socket_connect(zlookup) < 0) { + ++zlookup->fail; + zlog_warn("%s: failure connecting zclient socket: failures=%d", + __PRETTY_FUNCTION__, zlookup->fail); + } + else { + zlookup->fail = 0; /* reset counter on connection */ + } + + zassert(!zlookup->t_connect); + if (zlookup->sock < 0) { + /* Since last connect failed, retry within 10 secs */ + zclient_lookup_sched(zlookup, 10); + return -1; + } + + return 0; +} + +/* Schedule connection with delay. */ +static void zclient_lookup_sched(struct zclient *zlookup, int delay) +{ + zassert(!zlookup->t_connect); + + THREAD_TIMER_ON(master, zlookup->t_connect, + zclient_lookup_connect, + zlookup, delay); + + zlog_notice("%s: zclient lookup connection scheduled for %d seconds", + __PRETTY_FUNCTION__, delay); +} + +/* Schedule connection for now. */ +static void zclient_lookup_sched_now(struct zclient *zlookup) +{ + zassert(!zlookup->t_connect); + + zlookup->t_connect = thread_add_event(master, zclient_lookup_connect, + zlookup, 0); + + zlog_notice("%s: zclient lookup immediate connection scheduled", + __PRETTY_FUNCTION__); +} + +/* Schedule reconnection, if needed. */ +static void zclient_lookup_reconnect(struct zclient *zlookup) +{ + if (zlookup->t_connect) { + return; + } + + zclient_lookup_sched_now(zlookup); +} + +static void zclient_lookup_failed(struct zclient *zlookup) +{ + if (zlookup->sock >= 0) { + if (close(zlookup->sock)) { + zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock, + errno, safe_strerror(errno)); + } + zlookup->sock = -1; + } + + zclient_lookup_reconnect(zlookup); +} + +struct zclient *zclient_lookup_new() +{ + struct zclient *zlookup; + + zlookup = zclient_new(); + if (!zlookup) { + zlog_err("%s: zclient_new() failure", + __PRETTY_FUNCTION__); + return 0; + } + + zlookup->sock = -1; + zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zlookup->t_connect = 0; + + zclient_lookup_sched_now(zlookup); + + zlog_notice("%s: zclient lookup socket initialized", + __PRETTY_FUNCTION__); + + return zlookup; +} + +static int zclient_read_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr) +{ + int num_ifindex = 0; + struct stream *s; + const uint16_t MIN_LEN = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */ + uint16_t length, len; + u_char marker; + u_char version; + uint16_t command; + int nbytes; + struct in_addr raddr; + uint8_t distance; + uint32_t metric; + int nexthop_num; + int i; + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: addr=%s", + __PRETTY_FUNCTION__, + addr_str); + } + + s = zlookup->ibuf; + stream_reset(s); + + nbytes = stream_read(s, zlookup->sock, 2); + if (nbytes < 2) { + zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d", + __FILE__, __PRETTY_FUNCTION__, nbytes); + zclient_lookup_failed(zlookup); + return -1; + } + length = stream_getw(s); + + len = length - 2; + + if (len < MIN_LEN) { + zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d", + __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN); + zclient_lookup_failed(zlookup); + return -2; + } + + nbytes = stream_read(s, zlookup->sock, len); + if (nbytes < (length - 2)) { + zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d", + __FILE__, __PRETTY_FUNCTION__, nbytes, len); + zclient_lookup_failed(zlookup); + return -3; + } + marker = stream_getc(s); + version = stream_getc(s); + + if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) { + zlog_err("%s: socket %d version mismatch, marker %d, version %d", + __func__, zlookup->sock, marker, version); + return -4; + } + + command = stream_getw(s); + if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB) { + zlog_err("%s: socket %d command mismatch: %d", + __func__, zlookup->sock, command); + return -5; + } + + raddr.s_addr = stream_get_ipv4(s); + + if (raddr.s_addr != addr.s_addr) { + char addr_str[100]; + char raddr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", raddr, raddr_str, sizeof(raddr_str)); + zlog_warn("%s: address mismatch: addr=%s raddr=%s", + __PRETTY_FUNCTION__, + addr_str, raddr_str); + /* warning only */ + } + + distance = stream_getc(s); + metric = stream_getl(s); + nexthop_num = stream_getc(s); + + if (nexthop_num < 1) { + zlog_err("%s: socket %d bad nexthop_num=%d", + __func__, zlookup->sock, nexthop_num); + return -6; + } + + len -= MIN_LEN; + + for (i = 0; i < nexthop_num; ++i) { + enum nexthop_types_t nexthop_type; + + if (len < 1) { + zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d", + __func__, zlookup->sock, len); + return -7; + } + + nexthop_type = stream_getc(s); + --len; + + switch (nexthop_type) { + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + if (num_ifindex >= tab_size) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", + __FILE__, __PRETTY_FUNCTION__, + (num_ifindex + 1), tab_size, addr_str); + return num_ifindex; + } + if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) { + if (len < 4) { + zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d", + __func__, zlookup->sock, len); + return -8; + } + nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); + len -= 4; + } + else { + nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + } + nexthop_tab[num_ifindex].ifindex = stream_getl(s); + nexthop_tab[num_ifindex].protocol_distance = distance; + nexthop_tab[num_ifindex].route_metric = metric; + ++num_ifindex; + break; + case ZEBRA_NEXTHOP_IPV4: + if (num_ifindex >= tab_size) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", + __FILE__, __PRETTY_FUNCTION__, + (num_ifindex + 1), tab_size, addr_str); + return num_ifindex; + } + nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); + len -= 4; + nexthop_tab[num_ifindex].ifindex = 0; + nexthop_tab[num_ifindex].protocol_distance = distance; + nexthop_tab[num_ifindex].route_metric = metric; + { + char addr_str[100]; + char nexthop_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + zlog_warn("%s %s: zebra returned recursive nexthop %s for address %s", + __FILE__, __PRETTY_FUNCTION__, + nexthop_str, addr_str); + } + ++num_ifindex; + break; + default: + /* do nothing */ + { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s", + __FILE__, __PRETTY_FUNCTION__, + nexthop_type, addr_str); + } + break; + } + } + + return num_ifindex; +} + +static int zclient_lookup_nexthop_once(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr) +{ + struct stream *s; + int ret; + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: addr=%s", + __PRETTY_FUNCTION__, + addr_str); + } + + /* Check socket. */ + if (zlookup->sock < 0) { + zlog_err("%s %s: zclient lookup socket is not connected", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_failed(zlookup); + return -1; + } + + s = zlookup->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB); + stream_put_in_addr(s, &addr); + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = writen(zlookup->sock, s->data, stream_get_endp(s)); + if (ret < 0) { + zlog_err("%s %s: writen() failure writing to zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_failed(zlookup); + return -2; + } + if (ret == 0) { + zlog_err("%s %s: connection closed on zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_failed(zlookup); + return -3; + } + + return zclient_read_nexthop(zlookup, nexthop_tab, + tab_size, addr); +} + +int zclient_lookup_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr, + int max_lookup) +{ + int lookup; + uint32_t route_metric = 0xFFFFFFFF; + uint8_t protocol_distance = 0xFF; + + for (lookup = 0; lookup < max_lookup; ++lookup) { + int num_ifindex; + int first_ifindex; + struct in_addr nexthop_addr; + + num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, addr_str); + return -1; + } + + if (lookup < 1) { + /* this is the non-recursive lookup - save original metric/distance */ + route_metric = nexthop_tab[0].route_metric; + protocol_distance = nexthop_tab[0].protocol_distance; + } + + /* + FIXME: Non-recursive nexthop ensured only for first ifindex. + However, recursive route lookup should really be fixed in zebra daemon. + See also TODO T24. + */ + first_ifindex = nexthop_tab[0].ifindex; + nexthop_addr = nexthop_tab[0].nexthop_addr; + if (first_ifindex > 0) { + /* found: first ifindex is non-recursive nexthop */ + + if (lookup > 0) { + /* Report non-recursive success after first lookup */ + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, first_ifindex, addr_str, + nexthop_tab[0].protocol_distance, + nexthop_tab[0].route_metric); + + /* use last address as nexthop address */ + nexthop_tab[0].nexthop_addr = addr; + + /* report original route metric/distance */ + nexthop_tab[0].route_metric = route_metric; + nexthop_tab[0].protocol_distance = protocol_distance; + } + + return num_ifindex; + } + + { + char addr_str[100]; + char nexthop_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", nexthop_addr, nexthop_str, sizeof(nexthop_str)); + zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, nexthop_str, addr_str, + nexthop_tab[0].protocol_distance, + nexthop_tab[0].route_metric); + } + + addr = nexthop_addr; /* use nexthop addr for recursive lookup */ + + } /* for (max_lookup) */ + + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, addr_str); + + return -2; +} diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h new file mode 100644 index 00000000..1f184942 --- /dev/null +++ b/pimd/pim_zlookup.h @@ -0,0 +1,47 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ZLOOKUP_H +#define PIM_ZLOOKUP_H + +#include + +#include "zclient.h" + +#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */ + +struct pim_zlookup_nexthop { + struct in_addr nexthop_addr; + int ifindex; + uint32_t route_metric; + uint8_t protocol_distance; +}; + +struct zclient *zclient_lookup_new(void); + +int zclient_lookup_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr, + int max_lookup); + +#endif /* PIM_ZLOOKUP_H */ diff --git a/pimd/pimd.c b/pimd/pimd.c new file mode 100644 index 00000000..855defcc --- /dev/null +++ b/pimd/pimd.c @@ -0,0 +1,141 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_cmd.h" +#include "pim_iface.h" +#include "pim_zebra.h" +#include "pim_str.h" +#include "pim_oil.h" +#include "pim_pim.h" +#include "pim_upstream.h" +#include "pim_rand.h" +#include "pim_rpf.h" +#include "pim_ssmpingd.h" + +const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; +const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; +const char *const PIM_ALL_PIM_ROUTERS = MCAST_ALL_PIM_ROUTERS; +const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS; + +struct thread_master *master = 0; +uint32_t qpim_debugs = 0; +int qpim_mroute_socket_fd = -1; +int64_t qpim_mroute_socket_creation = 0; /* timestamp of creation */ +struct thread *qpim_mroute_socket_reader = 0; +int qpim_mroute_oif_highest_vif_index = -1; +struct list *qpim_channel_oil_list = 0; +int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */ +struct list *qpim_upstream_list = 0; +struct zclient *qpim_zclient_update = 0; +struct zclient *qpim_zclient_lookup = 0; +struct pim_assert_metric qpim_infinite_assert_metric; +long qpim_rpf_cache_refresh_delay_msec = 10000; +struct thread *qpim_rpf_cache_refresher = 0; +int64_t qpim_rpf_cache_refresh_requests = 0; +int64_t qpim_rpf_cache_refresh_events = 0; +int64_t qpim_rpf_cache_refresh_last = 0; +struct in_addr qpim_inaddr_any; +struct list *qpim_ssmpingd_list = 0; +struct in_addr qpim_ssmpingd_group_addr; +int64_t qpim_scan_oil_events = 0; +int64_t qpim_scan_oil_last = 0; +int64_t qpim_mroute_add_events = 0; +int64_t qpim_mroute_add_last = 0; +int64_t qpim_mroute_del_events = 0; +int64_t qpim_mroute_del_last = 0; + +static void pim_free() +{ + pim_ssmpingd_destroy(); + + if (qpim_channel_oil_list) + list_free(qpim_channel_oil_list); + + if (qpim_upstream_list) + list_free(qpim_upstream_list); +} + +void pim_init() +{ + pim_rand_init(); + + if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) { + zlog_err("%s %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + PIM_ALL_PIM_ROUTERS, errno, safe_strerror(errno)); + zassert(0); + return; + } + + qpim_channel_oil_list = list_new(); + if (!qpim_channel_oil_list) { + zlog_err("%s %s: failure: channel_oil_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return; + } + qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free; + + qpim_upstream_list = list_new(); + if (!qpim_upstream_list) { + zlog_err("%s %s: failure: upstream_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + pim_free(); + return; + } + qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free; + + qpim_mroute_socket_fd = -1; /* mark mroute as disabled */ + qpim_mroute_oif_highest_vif_index = -1; + + zassert(!qpim_debugs); + zassert(!PIM_MROUTE_IS_ENABLED); + + qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY; + + /* + RFC 4601: 4.6.3. Assert Metrics + + assert_metric + infinite_assert_metric() { + return {1,infinity,infinity,0} + } + */ + qpim_infinite_assert_metric.rpt_bit_flag = 1; + qpim_infinite_assert_metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX; + qpim_infinite_assert_metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX; + qpim_infinite_assert_metric.ip_address = qpim_inaddr_any; + + pim_if_init(); + pim_cmd_init(); + pim_ssmpingd_init(); +} + +void pim_terminate() +{ + pim_free(); +} diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample new file mode 100644 index 00000000..67530856 --- /dev/null +++ b/pimd/pimd.conf.sample @@ -0,0 +1,41 @@ +! +! pimd sample configuration file +! $QuaggaId: $Format:%an, %ai, %h$ $ +! +hostname quagga-pimd-router +password zebra +!enable password zebra +! +!log file pimd.log +log stdout +! +line vty + exec-timeout 60 +! +!debug igmp +!debug pim +!debug pim zebra +! +ip multicast-routing +! +! ! You may want to enable ssmpingd for troubleshooting +! ! See http://www.venaas.no/multicast/ssmping/ +! ! +! ip ssmpingd 1.1.1.1 +! ip ssmpingd 2.2.2.2 +! +! ! HINTS: +! ! - Enable "ip pim ssm" on the interface directly attached to the +! ! multicast source host (if this is the first-hop router) +! ! - Enable "ip pim ssm" on pim-routers-facing interfaces +! ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces +! ! - In order to inject IGMPv3 local membership information in the +! ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on +! ! the same interface; otherwise PIM won't advertise +! ! IGMPv3-learned membership to other PIM routers +! +interface eth0 + ip pim ssm + ip igmp + +! -x- diff --git a/pimd/pimd.h b/pimd/pimd.h new file mode 100644 index 00000000..22a29220 --- /dev/null +++ b/pimd/pimd.h @@ -0,0 +1,157 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIMD_H +#define PIMD_H + +#include + +#include "pim_mroute.h" +#include "pim_assert.h" + +#define PIMD_PROGNAME "pimd" +#define PIMD_DEFAULT_CONFIG "pimd.conf" +#define PIMD_VTY_PORT 2611 +#define PIMD_BUG_ADDRESS "https://github.com/udhos/qpimd" + +#define PIM_IP_HEADER_MIN_LEN (20) +#define PIM_IP_HEADER_MAX_LEN (60) +#define PIM_IP_PROTO_IGMP (2) +#define PIM_IP_PROTO_PIM (103) +#define PIM_IGMP_MIN_LEN (8) +#define PIM_MSG_HEADER_LEN (4) +#define PIM_PIM_MIN_LEN PIM_MSG_HEADER_LEN +#define PIM_PROTO_VERSION (2) + +#define MCAST_ALL_SYSTEMS "224.0.0.1" +#define MCAST_ALL_ROUTERS "224.0.0.2" +#define MCAST_ALL_PIM_ROUTERS "224.0.0.13" +#define MCAST_ALL_IGMP_ROUTERS "224.0.0.22" + +#define PIM_FORCE_BOOLEAN(expr) ((expr) != 0) + +#define PIM_NET_INADDR_ANY (htonl(INADDR_ANY)) +#define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */ +#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */ + +#define PIM_MASK_PIM_EVENTS (1 << 0) +#define PIM_MASK_PIM_PACKETS (1 << 1) +#define PIM_MASK_PIM_PACKETDUMP_SEND (1 << 2) +#define PIM_MASK_PIM_PACKETDUMP_RECV (1 << 3) +#define PIM_MASK_PIM_TRACE (1 << 4) +#define PIM_MASK_IGMP_EVENTS (1 << 5) +#define PIM_MASK_IGMP_PACKETS (1 << 6) +#define PIM_MASK_IGMP_TRACE (1 << 7) +#define PIM_MASK_ZEBRA (1 << 8) +#define PIM_MASK_SSMPINGD (1 << 9) +#define PIM_MASK_MROUTE (1 << 10) +#define PIM_MASK_PIM_HELLO (1 << 11) +#define PIM_MASK_PIM_J_P (1 << 12) + +const char *const PIM_ALL_SYSTEMS; +const char *const PIM_ALL_ROUTERS; +const char *const PIM_ALL_PIM_ROUTERS; +const char *const PIM_ALL_IGMP_ROUTERS; + +struct thread_master *master; +uint32_t qpim_debugs; +int qpim_mroute_socket_fd; +int64_t qpim_mroute_socket_creation; /* timestamp of creation */ +struct thread *qpim_mroute_socket_reader; +int qpim_mroute_oif_highest_vif_index; +struct list *qpim_channel_oil_list; /* list of struct channel_oil */ +struct in_addr qpim_all_pim_routers_addr; +int qpim_t_periodic; /* Period between Join/Prune Messages */ +struct list *qpim_upstream_list; /* list of struct pim_upstream */ +struct zclient *qpim_zclient_update; +struct zclient *qpim_zclient_lookup; +struct pim_assert_metric qpim_infinite_assert_metric; +long qpim_rpf_cache_refresh_delay_msec; +struct thread *qpim_rpf_cache_refresher; +int64_t qpim_rpf_cache_refresh_requests; +int64_t qpim_rpf_cache_refresh_events; +int64_t qpim_rpf_cache_refresh_last; +struct in_addr qpim_inaddr_any; +struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ +struct in_addr qpim_ssmpingd_group_addr; +int64_t qpim_scan_oil_events; +int64_t qpim_scan_oil_last; +int64_t qpim_mroute_add_events; +int64_t qpim_mroute_add_last; +int64_t qpim_mroute_del_events; +int64_t qpim_mroute_del_last; + +#define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) + +#define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0) +#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0) + +#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS) +#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS) +#define PIM_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE) +#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS) +#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS) +#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) +#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) +#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) +#define PIM_DEBUG_MROUTE (qpim_debugs & PIM_MASK_MROUTE) +#define PIM_DEBUG_PIM_HELLO (qpim_debugs & PIM_MASK_PIM_HELLO) +#define PIM_DEBUG_PIM_J_P (qpim_debugs & PIM_MASK_PIM_J_P) + +#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) +#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) +#define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE)) + +#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS) +#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS) +#define PIM_DO_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DO_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE) +#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS) +#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS) +#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) +#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) +#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) +#define PIM_DO_DEBUG_MROUTE (qpim_debugs |= PIM_MASK_MROUTE) +#define PIM_DO_DEBUG_PIM_HELLO (qpim_debugs |= PIM_MASK_PIM_HELLO) +#define PIM_DO_DEBUG_PIM_J_P (qpim_debugs |= PIM_MASK_PIM_J_P) + +#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) +#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) +#define PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE) +#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS) +#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS) +#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) +#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) +#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) +#define PIM_DONT_DEBUG_MROUTE (qpim_debugs &= ~PIM_MASK_MROUTE) +#define PIM_DONT_DEBUG_PIM_HELLO (qpim_debugs &= ~PIM_MASK_PIM_HELLO) +#define PIM_DONT_DEBUG_PIM_J_P (qpim_debugs &= ~PIM_MASK_PIM_J_P) + +void pim_init(void); +void pim_terminate(void); + +#endif /* PIMD_H */ diff --git a/pimd/quagga-bootstrap.sh b/pimd/quagga-bootstrap.sh new file mode 100755 index 00000000..4ec443d9 --- /dev/null +++ b/pimd/quagga-bootstrap.sh @@ -0,0 +1,23 @@ +#! /bin/bash +# +# Bootstrap Quagga autotools for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-bootstrap.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +me=`basename $0` +msg () { + echo >&2 $me: $* +} + +if [ -f ./bootstrap.sh ]; then + msg found ./bootstrap.sh from quagga + ./bootstrap.sh +else + msg missing ./bootstrap.sh from quagga + #autoreconf -i --force + #bootstrap from tarball prefers autoreconf -i + autoreconf -i +fi diff --git a/pimd/quagga-build-no-vtysh.sh b/pimd/quagga-build-no-vtysh.sh new file mode 100755 index 00000000..7136f670 --- /dev/null +++ b/pimd/quagga-build-no-vtysh.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Build minimum Quagga needed for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-build-no-vtysh.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure-no-vtysh.sh && make diff --git a/pimd/quagga-build.sh b/pimd/quagga-build.sh new file mode 100755 index 00000000..2ad476d0 --- /dev/null +++ b/pimd/quagga-build.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Build minimum Quagga needed for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-build.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure.sh && make diff --git a/pimd/quagga-configure-no-vtysh.sh b/pimd/quagga-configure-no-vtysh.sh new file mode 100755 index 00000000..b3052dce --- /dev/null +++ b/pimd/quagga-configure-no-vtysh.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Configure for minimum Quagga build needed for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-configure-no-vtysh.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./configure --disable-babeld --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh new file mode 100755 index 00000000..72329eb9 --- /dev/null +++ b/pimd/quagga-configure.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Configure for minimum Quagga build needed for pimd. +# +# Run from quagga's top dir as: +# . pimd/quagga-configure.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +tail -1 ./pimd/quagga-configure-no-vtysh.sh --enable-vtysh diff --git a/pimd/quagga-git-add.sh b/pimd/quagga-git-add.sh new file mode 100755 index 00000000..3824e984 --- /dev/null +++ b/pimd/quagga-git-add.sh @@ -0,0 +1,12 @@ +#! /bin/bash +# +# Add to git new files created by qpimd patch +# +# Run from quagga's top dir as: +# ./pimd/quagga-git-add.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +chmod a+rx pimd/*.sh +git add doc/pimd.8 +git add pimd diff --git a/pimd/quagga-memtypes.sh b/pimd/quagga-memtypes.sh new file mode 100755 index 00000000..e86f414b --- /dev/null +++ b/pimd/quagga-memtypes.sh @@ -0,0 +1,22 @@ +#! /bin/bash +# +# Check lib/memtypes.h from Quagga +# +# Run from quagga's top dir as: +# ./pimd/quagga-memtypes.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +me=`basename $0` +msg () { + echo >&2 $me: $* +} + +memtypes_h=lib/memtypes.h +if [ -e $memtypes_h ]; then + memtypes_h_size=`ls -s $memtypes_h | cut -d' ' -f1` + if [ "$memtypes_h_size" -lt 1 ]; then + msg WARNING: removing empty file: $memtypes_h -- awk failed? + rm $memtypes_h + fi +fi diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c new file mode 100644 index 00000000..fe64fbc0 --- /dev/null +++ b/pimd/test_igmpv3_join.c @@ -0,0 +1,149 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program 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 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pim_igmp_join.h" + +const char *prog_name = 0; + +static int iface_solve_index(const char *ifname) +{ + struct if_nameindex *ini; + int ifindex = -1; + int i; + + if (!ifname) + return -1; + + ini = if_nameindex(); + if (!ini) { + int err = errno; + fprintf(stderr, + "%s: interface=%s: failure solving index: errno=%d: %s\n", + prog_name, ifname, err, strerror(err)); + errno = err; + return -1; + } + + for (i = 0; ini[i].if_index; ++i) { +#if 0 + fprintf(stderr, + "%s: interface=%s matching against local ifname=%s ifindex=%d\n", + prog_name, ifname, ini[i].if_name, ini[i].if_index); +#endif + if (!strcmp(ini[i].if_name, ifname)) { + ifindex = ini[i].if_index; + break; + } + } + + if_freenameindex(ini); + + return ifindex; +} + +int main(int argc, const char *argv[]) +{ + struct in_addr group_addr; + struct in_addr source_addr; + const char *ifname; + const char *group; + const char *source; + int ifindex; + int result; + int fd; + + prog_name = argv[0]; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + fprintf(stderr, + "%s: could not create socket: socket(): errno=%d: %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } + + if (argc != 4) { + fprintf(stderr, + "usage: %s interface group source\n" + "example: %s eth0 232.1.1.1 1.1.1.1\n", + prog_name, prog_name); + exit(1); + } + + ifname = argv[1]; + group = argv[2]; + source = argv[3]; + + ifindex = iface_solve_index(ifname); + if (ifindex < 0) { + fprintf(stderr, "%s: could not find interface: %s\n", + prog_name, ifname); + exit(1); + } + + result = inet_pton(AF_INET, group, &group_addr); + if (result <= 0) { + fprintf(stderr, "%s: bad group address: %s\n", + prog_name, group); + exit(1); + } + + result = inet_pton(AF_INET, source, &source_addr); + if (result <= 0) { + fprintf(stderr, "%s: bad source address: %s\n", + prog_name, source); + exit(1); + } + + result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr); + if (result) { + fprintf(stderr, + "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n", + prog_name, fd, group, source, ifindex, ifname, + errno, strerror(errno)); + exit(1); + } + + printf("%s: joined channel (S,G)=(%s,%s) on interface %s\n", + prog_name, source, group, ifname); + + printf("%s: waiting...\n", prog_name); + + getchar(); + + close(fd); + + printf("%s: left channel (S,G)=(%s,%s) on interface %s\n", + prog_name, source, group, ifname); + + exit(0); +} diff --git a/ports/Makefile b/ports/Makefile index d085d06e..86f77bd0 100644 --- a/ports/Makefile +++ b/ports/Makefile @@ -54,5 +54,6 @@ post-install: @echo " ripngd 2603/tcp # RIPngd vty"; @echo " ospfd 2604/tcp # OSPFd vty"; @echo " bgpd 2605/tcp # BGPd vty"; + @echo " pimd 2611/tcp # PIMd vty"; .include diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR index 0c8d01b7..aeb1950e 100644 --- a/ports/pkg/DESCR +++ b/ports/pkg/DESCR @@ -47,6 +47,7 @@ ripd 2602/tcp # RIPd vty ripngd 2603/tcp # RIPngd vty ospfd 2604/tcp # OSPFd vty bgpd 2605/tcp # BGPd vty +pimd 2611/tcp # PIMd vty I recommend you to add upper list to /etc/services. diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index a8c7fbf3..75835bbe 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -44,7 +44,7 @@ %{!?dist: %define dist %{default_dist}} # as distros change packages we depend on, our Requires have to change, sadly. -%define quagga_buildreqs texinfo tetex autoconf pam-devel +%define quagga_buildreqs texi2html texinfo tetex autoconf pam-devel %define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel # FC4 and 5 split texi2html out of tetex package. @@ -68,7 +68,7 @@ %define daemon_list zebra ripd ospfd bgpd %if %{with_ipv6} -%define daemonv6_list ripngd ospf6d +%define daemonv6_list ripngd babeld ospf6d %else %define daemonv6_list "" %endif @@ -97,17 +97,17 @@ Source0: http://www.quagga.net/snapshots/cvs/%{name}-%{version}.tar.gz URL: http://www.quagga.net %if %{with_snmp} BuildRequires: net-snmp-devel -Prereq: net-snmp +Requires(pre): net-snmp %endif %if %{with_vtysh} BuildRequires: readline readline-devel ncurses ncurses-devel -Prereq: ncurses +Requires(pre): ncurses %endif BuildRequires: texinfo tetex autoconf pam-devel patch libcap-devel tetex # Initscripts > 5.60 is required for IPv6 support -Prereq: initscripts >= 5.60 -Prereq: ncurses pam -Prereq: /sbin/install-info +Requires(pre): initscripts >= 5.60 +Requires(pre): ncurses pam +Requires(pre): /sbin/install-info Provides: routingdaemon BuildRoot: %{_tmppath}/%{name}-%{version}-root Obsoletes: bird gated mrt zebra @@ -303,6 +303,9 @@ zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" %if %{with_isisd} zebra_spec_add_service isisd 2608/tcp "ISISd vty" %endif +%if %{with_pimd} +zebra_spec_add_service pimd 2611/tcp "PIMd vty" +%endif for daemon in %daemon_list ; do /sbin/chkconfig --add ${daemon} @@ -406,6 +409,7 @@ rm -rf $RPM_BUILD_ROOT %if %{with_ipv6} %{_sbindir}/ripngd %{_sbindir}/ospf6d +%{_sbindir}/babeld %endif %if %{with_isisd} %{_sbindir}/isisd diff --git a/ripd/Makefile.am b/ripd/Makefile.am index b0bc7a87..65a626dd 100644 --- a/ripd/Makefile.am +++ b/ripd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c index 662e5843..a267874a 100644 --- a/ripd/rip_debug.c +++ b/ripd/rip_debug.c @@ -27,7 +27,7 @@ unsigned long rip_debug_event = 0; unsigned long rip_debug_packet = 0; unsigned long rip_debug_zebra = 0; - + DEFUN (show_debugging_rip, show_debugging_rip_cmd, "show debugging rip", diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 810b71c0..35685a75 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -41,7 +41,7 @@ #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" - + /* static prototypes */ static void rip_enable_apply (struct interface *); static void rip_passive_interface_apply (struct interface *); @@ -49,8 +49,8 @@ static int rip_if_down(struct interface *ifp); static int rip_enable_if_lookup (const char *ifname); static int rip_enable_network_lookup2 (struct connected *connected); static void rip_enable_apply_all (void); - -struct message ri_version_msg[] = + +const struct message ri_version_msg[] = { {RI_RIP_VERSION_1, "1"}, {RI_RIP_VERSION_2, "2"}, @@ -68,7 +68,7 @@ struct route_table *rip_enable_network; /* Vector to store passive-interface name. */ static int passive_default; /* are we in passive-interface default mode? */ vector Vrip_passive_nondefault; - + /* Join to the RIP version 2 multicast group. */ static int ipv4_multicast_join (int sock, @@ -109,7 +109,7 @@ ipv4_multicast_leave (int sock, return ret; } - + /* Allocate new RIP's interface configuration. */ static struct rip_interface * rip_interface_new (void) @@ -577,37 +577,15 @@ rip_if_down(struct interface *ifp) struct route_node *rp; struct rip_info *rinfo; struct rip_interface *ri = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL, *nextnode = NULL; if (rip) - { - for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - { - /* Routes got through this interface. */ - if (rinfo->ifindex == ifp->ifindex && - rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->sub_type == RIP_ROUTE_RTE) - { - rip_zebra_ipv4_delete ((struct prefix_ipv4 *) &rp->p, - &rinfo->nexthop, - rinfo->metric); - - rip_redistribute_delete (rinfo->type,rinfo->sub_type, - (struct prefix_ipv4 *)&rp->p, - rinfo->ifindex); - } - else - { - /* All redistributed routes but static and system */ - if ((rinfo->ifindex == ifp->ifindex) && - /* (rinfo->type != ZEBRA_ROUTE_STATIC) && */ - (rinfo->type != ZEBRA_ROUTE_SYSTEM)) - rip_redistribute_delete (rinfo->type,rinfo->sub_type, - (struct prefix_ipv4 *)&rp->p, - rinfo->ifindex); - } - } - } - + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo)) + if (rinfo->ifindex == ifp->ifindex) + rip_ecmp_delete (rinfo); + ri = ifp->info; if (ri->running) @@ -754,7 +732,7 @@ rip_interface_address_delete (int command, struct zclient *zclient, return 0; } - + /* Check interface is enabled by network statement. */ /* Check wether the interface has at least a connected prefix that * is within the ripng_enable_network table. */ @@ -1142,7 +1120,7 @@ rip_clean_network () vector_slot (rip_enable_interface, i) = NULL; } } - + /* Utility function for looking up passive interface settings. */ static int rip_passive_nondefault_lookup (const char *ifname) @@ -1229,7 +1207,7 @@ rip_passive_nondefault_clean (void) } rip_passive_interface_apply_all (); } - + /* RIP enable network or interface configuration. */ DEFUN (rip_network, rip_network_cmd, @@ -1913,7 +1891,7 @@ DEFUN (no_rip_passive_interface, else return rip_passive_nondefault_unset (vty, ifname); } - + /* Write rip configuration of each interface. */ static int rip_interface_config_write (struct vty *vty) diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 6a9fa71d..e81e61b8 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -126,7 +126,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -183,7 +183,7 @@ static struct quagga_signal_t ripd_signals[] = .handler = &sigint, }, }; - + /* Main routine of ripd. */ int main (int argc, char **argv) @@ -287,9 +287,6 @@ main (int argc, char **argv) rip_zclient_init (); rip_peer_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c index fd912eba..6a3add64 100644 --- a/ripd/rip_peer.c +++ b/ripd/rip_peer.c @@ -32,7 +32,7 @@ /* Linked list of RIP peer. */ struct list *peer_list; - + static struct rip_peer * rip_peer_new (void) { diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index cb87ea55..37a986c5 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -32,7 +32,7 @@ #include "plist.h" #include "ripd/ripd.h" - + struct rip_metric_modifier { enum @@ -160,7 +160,7 @@ rip_route_map_update (const char *notused) } } } - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -323,7 +323,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -370,7 +370,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return @@ -416,7 +416,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -507,7 +507,7 @@ struct route_map_rule_cmd route_match_tag_cmd = route_match_tag_compile, route_match_tag_free }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -718,7 +718,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = route_set_tag_compile, route_set_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 090ebfae..2df815b0 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -33,7 +33,7 @@ #include "smux.h" #include "ripd/ripd.h" - + /* RIPv2-MIB. */ #define RIPV2MIB 1,3,6,1,2,1,23 @@ -76,7 +76,7 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Define SNMP local variables. */ SNMP_LOCAL_VARIABLES @@ -149,7 +149,7 @@ struct variable rip_variables[] = }; extern struct thread_master *master; - + static u_char * rip2Globals (struct variable *v, oid name[], size_t *length, int exact, size_t *var_len, WriteMethod **write_method) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 199e85e8..0e1e52eb 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -23,7 +23,9 @@ #include "command.h" #include "prefix.h" +#include "table.h" #include "stream.h" +#include "memory.h" #include "routemap.h" #include "zclient.h" #include "log.h" @@ -33,13 +35,19 @@ /* All information about zebra. */ struct zclient *zclient = NULL; - -/* RIPd to zebra command interface. */ -void -rip_zebra_ipv4_add (struct prefix_ipv4 *p, struct in_addr *nexthop, - u_int32_t metric, u_char distance) + +/* Send ECMP routes to zebra. */ +static void +rip_zebra_ipv4_send (struct route_node *rp, u_char cmd) { + static struct in_addr **nexthops = NULL; + static unsigned int nexthops_len = 0; + + struct list *list = (struct list *)rp->info; struct zapi_ipv4 api; + struct listnode *listnode = NULL; + struct rip_info *rinfo = NULL; + int count = 0; if (zclient->redist[ZEBRA_ROUTE_RIP]) { @@ -47,48 +55,72 @@ rip_zebra_ipv4_add (struct prefix_ipv4 *p, struct in_addr *nexthop, api.flags = 0; api.message = 0; api.safi = SAFI_UNICAST; + + if (nexthops_len < listcount (list)) + { + nexthops_len = listcount (list); + nexthops = XREALLOC (MTYPE_TMP, nexthops, + nexthops_len * sizeof (struct in_addr *)); + } + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + nexthops[count++] = &rinfo->nexthop; + if (cmd == ZEBRA_IPV4_ROUTE_ADD) + SET_FLAG (rinfo->flags, RIP_RTF_FIB); + else + UNSET_FLAG (rinfo->flags, RIP_RTF_FIB); + } + + api.nexthop = nexthops; + api.nexthop_num = count; api.ifindex_num = 0; + + rinfo = listgetdata (listhead (list)); + SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; + api.metric = rinfo->metric; - if (distance && distance != ZEBRA_RIP_DISTANCE_DEFAULT) - { - SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); - api.distance = distance; - } + if (rinfo->distance && rinfo->distance != ZEBRA_RIP_DISTANCE_DEFAULT) + { + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = rinfo->distance; + } - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, p, &api); + zapi_ipv4_route (cmd, zclient, + (struct prefix_ipv4 *)&rp->p, &api); + + if (IS_RIP_DEBUG_ZEBRA) + { + if (rip->ecmp) + zlog_debug ("%s: %s/%d nexthops %d", + (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen, count); + else + zlog_debug ("%s: %s/%d", + (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen); + } rip_global_route_changes++; } } +/* Add/update ECMP routes to zebra. */ void -rip_zebra_ipv4_delete (struct prefix_ipv4 *p, struct in_addr *nexthop, - u_int32_t metric) +rip_zebra_ipv4_add (struct route_node *rp) { - struct zapi_ipv4 api; - - if (zclient->redist[ZEBRA_ROUTE_RIP]) - { - api.type = ZEBRA_ROUTE_RIP; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; - api.ifindex_num = 0; - SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; - - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, p, &api); + rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_ADD); +} - rip_global_route_changes++; - } +/* Delete ECMP routes from zebra. */ +void +rip_zebra_ipv4_delete (struct route_node *rp) +{ + rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_DELETE); } /* Zebra route add and delete treatment. */ @@ -196,7 +228,7 @@ rip_routemap_unset (int type, const char *name) return 0; } - + /* Redistribution types */ static struct { int type; @@ -206,9 +238,12 @@ static struct { {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, {ZEBRA_ROUTE_CONNECT, 1, "connected"}, {ZEBRA_ROUTE_STATIC, 1, "static"}, - {ZEBRA_ROUTE_OSPF, 1, "ospf"}, + {ZEBRA_ROUTE_OSPF, 2, "ospf"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, - {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, {0, 0, NULL} }; @@ -551,7 +586,7 @@ DEFUN (no_rip_redistribute_type_metric_routemap, return CMD_WARNING; } - + /* Default information originate. */ DEFUN (rip_default_information_originate, @@ -597,7 +632,7 @@ DEFUN (no_rip_default_information_originate, return CMD_SUCCESS; } - + /* RIP configuration write function. */ static int config_write_zebra (struct vty *vty) diff --git a/ripd/ripd.c b/ripd/ripd.c index db29d54a..9d355ecb 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -61,20 +61,20 @@ long rip_global_route_changes = 0; /* RIP queries. */ long rip_global_queries = 0; - + /* Prototypes. */ static void rip_event (enum rip_event, int); static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char); static int rip_triggered_update (struct thread *); static int rip_update_jitter (unsigned long); - + /* RIP output routes type. */ enum { rip_all_route, rip_changed_route }; - + /* RIP command strings. */ static const struct message rip_msg[] = { @@ -86,7 +86,7 @@ static const struct message rip_msg[] = {RIP_POLL_ENTRY, "POLL ENTRY"}, {0, NULL}, }; - + /* Utility function to set boradcast option to the socket. */ static int sockopt_broadcast (int sock) @@ -138,8 +138,13 @@ rip_garbage_collect (struct thread *t) rp = rinfo->rp; /* Unlock route_node. */ - rp->info = NULL; - route_unlock_node (rp); + listnode_delete (rp->info, rinfo); + if (list_isempty ((struct list *)rp->info)) + { + list_free (rp->info); + rp->info = NULL; + route_unlock_node (rp); + } /* Free RIP routing information. */ rip_info_free (rinfo); @@ -147,36 +152,155 @@ rip_garbage_collect (struct thread *t) return 0; } -/* Timeout RIP routes. */ -static int -rip_timeout (struct thread *t) +static void rip_timeout_update (struct rip_info *rinfo); + +/* Add new route to the ECMP list. + * RETURN: the new entry added in the list, or NULL if it is not the first + * entry and ECMP is not allowed. + */ +struct rip_info * +rip_ecmp_add (struct rip_info *rinfo_new) { - struct rip_info *rinfo; - struct route_node *rn; + struct route_node *rp = rinfo_new->rp; + struct rip_info *rinfo = NULL; + struct list *list = NULL; - rinfo = THREAD_ARG (t); - rinfo->t_timeout = NULL; + if (rp->info == NULL) + rp->info = list_new (); + list = (struct list *)rp->info; - rn = rinfo->rp; + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !rip->ecmp) + return NULL; - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, - rip->garbage_time); + rinfo = rip_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + listnode_add (list, rinfo); - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop, - rinfo->metric); - /* - The metric for the route is set to 16 (infinity). This causes - the route to be removed from service. */ - rinfo->metric = RIP_METRIC_INFINITY; - rinfo->flags &= ~RIP_RTF_FIB; + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + rip_zebra_ipv4_add (rp); + } - /* - The route change flag is to indicate that this entry has been - changed. */ - rinfo->flags |= RIP_RTF_CHANGED; + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); - /* - The output process is signalled to trigger a response. */ + /* Signal the output process to trigger an update (see section 2.5). */ rip_event (RIP_TRIGGERED_UPDATE, 0); + return rinfo; +} + +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct rip_info * +rip_ecmp_replace (struct rip_info *rinfo_new) +{ + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct rip_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return rip_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (rip_route_rte (rinfo) && !rip_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + /* The ADD message implies an update. */ + rip_zebra_ipv4_add (rp); + } + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Delete one route from the ECMP list. + * RETURN: + * null - the entry is freed, and other entries exist in the list + * the entry - the entry is the last one in the list; its metric is set + * to INFINITY, and the garbage collector is started for it + */ +struct rip_info * +rip_ecmp_delete (struct rip_info *rinfo) +{ + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIP_TIMER_OFF (rinfo->t_timeout); + + if (listcount (list) > 1) + { + /* Some other ECMP entries still exist. Just delete this entry. */ + RIP_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + /* The ADD message implies the update. */ + rip_zebra_ipv4_add (rp); + rip_info_free (rinfo); + rinfo = NULL; + } + else + { + assert (rinfo == listgetdata (listhead (list))); + + /* This is the only entry left in the list. We must keep it in + * the list for garbage collection time, with INFINITY metric. */ + + rinfo->metric = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIP routes. */ +static int +rip_timeout (struct thread *t) +{ + rip_ecmp_delete ((struct rip_info *)THREAD_ARG (t)); return 0; } @@ -365,13 +489,13 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, int ret; struct prefix_ipv4 p; struct route_node *rp; - struct rip_info *rinfo, rinfotmp; + struct rip_info *rinfo = NULL, newinfo; struct rip_interface *ri; struct in_addr *nexthop; - u_char oldmetric; int same = 0; - int route_reuse = 0; unsigned char old_dist, new_dist; + struct list *list = NULL; + struct listnode *node = NULL; /* Make prefix structure. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -389,21 +513,20 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, if (ret < 0) return; + memset (&newinfo, 0, sizeof (newinfo)); + newinfo.type = ZEBRA_ROUTE_RIP; + newinfo.sub_type = RIP_ROUTE_RTE; + newinfo.nexthop = rte->nexthop; + newinfo.from = from->sin_addr; + newinfo.ifindex = ifp->ifindex; + newinfo.metric = rte->metric; + newinfo.metric_out = rte->metric; /* XXX */ + newinfo.tag = ntohs (rte->tag); /* XXX */ + /* Modify entry according to the interface routemap. */ if (ri->routemap[RIP_FILTER_IN]) { int ret; - struct rip_info newinfo; - - memset (&newinfo, 0, sizeof (newinfo)); - newinfo.type = ZEBRA_ROUTE_RIP; - newinfo.sub_type = RIP_ROUTE_RTE; - newinfo.nexthop = rte->nexthop; - newinfo.from = from->sin_addr; - newinfo.ifindex = ifp->ifindex; - newinfo.metric = rte->metric; - newinfo.metric_out = rte->metric; /* XXX */ - newinfo.tag = ntohs (rte->tag); /* XXX */ /* The object should be of the type of rip_info */ ret = route_map_apply (ri->routemap[RIP_FILTER_IN], @@ -433,7 +556,7 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* If offset-list does not modify the metric use interface's metric. */ if (!ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIP_METRIC_INFINITY) rte->metric = RIP_METRIC_INFINITY; @@ -455,8 +578,62 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* Get index for the prefix. */ rp = route_node_get (rip->table, (struct prefix *) &p); + newinfo.rp = rp; + newinfo.nexthop = *nexthop; + newinfo.metric = rte->metric; + newinfo.tag = ntohs (rte->tag); + newinfo.distance = rip_distance_apply (&newinfo); + + new_dist = newinfo.distance ? newinfo.distance : ZEBRA_RIP_DISTANCE_DEFAULT; + /* Check to see whether there is already RIP route on the table. */ - rinfo = rp->info; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + /* Need to compare with redistributed entry or local entry */ + if (!rip_route_rte (rinfo)) + break; + + if (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && + IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) + break; + + if (!listnextnode (node)) + { + /* Not found in the list */ + + if (rte->metric > rinfo->metric) + { + /* New route has a greater metric. Discard it. */ + route_unlock_node (rp); + return; + } + + if (rte->metric < rinfo->metric) + /* New route has a smaller metric. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics are same. We compare the distances. */ + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; + + if (new_dist > old_dist) + { + /* New route has a greater distance. Discard it. */ + route_unlock_node (rp); + return; + } + + if (new_dist < old_dist) + /* New route has a smaller distance. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics and distances are both same. Keep "rinfo" null and + * the new route is added in the ECMP list in below. */ + } + } if (rinfo) { @@ -474,101 +651,37 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->metric != RIP_METRIC_INFINITY) { - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - new_dist = rip_distance_apply (&rinfotmp); - new_dist = new_dist ? new_dist : ZEBRA_RIP_DISTANCE_DEFAULT; old_dist = rinfo->distance; - /* Only connected routes may have a valid NULL distance */ - if (rinfo->type != ZEBRA_ROUTE_CONNECT) + /* Only routes directly connected to an interface (nexthop == 0) + * may have a valid NULL distance */ + if (rinfo->nexthop.s_addr != 0) old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT; /* If imported route does not have STRICT precedence, mark it as a ghost */ - if (new_dist > old_dist - || rte->metric == RIP_METRIC_INFINITY) - { - route_unlock_node (rp); - return; - } - else - { - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - rip_info_free (rinfo); - rinfo = NULL; - route_reuse = 1; - } + if (new_dist <= old_dist && rte->metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + + route_unlock_node (rp); + return; } } if (!rinfo) { + if (rp->info) + route_unlock_node (rp); + /* Now, check to see whether there is already an explicit route for the destination prefix. If there is no such route, add this route to the routing table, unless the metric is infinity (there is no point in adding a route which unusable). */ if (rte->metric != RIP_METRIC_INFINITY) - { - rinfo = rip_info_new (); - - /* - Setting the destination prefix and length to those in - the RTE. */ - rinfo->rp = rp; - - /* - Setting the metric to the newly calculated metric (as - described above). */ - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - - /* - Set the next hop address to be the address of the router - from which the datagram came or the next hop address - specified by a next hop RTE. */ - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - - /* - Initialize the timeout for the route. If the - garbage-collection timer is running for this route, stop it - (see section 2.3 for a discussion of the timers). */ - rip_timeout_update (rinfo); - - /* - Set the route change flag. */ - rinfo->flags |= RIP_RTF_CHANGED; - - /* - Signal the output process to trigger an update (see section - 2.5). */ - rip_event (RIP_TRIGGERED_UPDATE, 0); - - /* Finally, route goes into the kernel. */ - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - /* Set distance value. */ - rinfo->distance = rip_distance_apply (rinfo); - - rp->info = rinfo; - rip_zebra_ipv4_add (&p, &rinfo->nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; - } - - /* Unlock temporary lock, i.e. same behaviour */ - if (route_reuse) - route_unlock_node (rp); + rip_ecmp_add (&newinfo); } else { /* Route is there but we are not sure the route is RIP or not. */ - rinfo = rp->info; /* If there is an existing route, compare the next hop address to the address of the router from which the datagram came. @@ -577,16 +690,8 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && (rinfo->ifindex == ifp->ifindex)); - if (same) - rip_timeout_update (rinfo); - - - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different @@ -598,95 +703,51 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, || (rte->metric < rinfo->metric) || ((same) && (rinfo->metric == rte->metric) - && ntohs (rte->tag) != rinfo->tag) - || (rinfo->distance > rip_distance_apply (&rinfotmp)) - || ((rinfo->distance != rip_distance_apply (rinfo)) && same)) + && (newinfo.tag != rinfo->tag)) + || (old_dist > new_dist) + || ((old_dist != new_dist) && same)) { - /* - Adopt the route from the datagram. That is, put the - new metric in, and adjust the next hop address (if - necessary). */ - oldmetric = rinfo->metric; - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - rinfo->distance = rip_distance_apply (rinfo); - - /* Should a new route to this network be established - while the garbage-collection timer is running, the - new route will replace the one that is about to be - deleted. In this case the garbage-collection timer - must be cleared. */ - - if (oldmetric == RIP_METRIC_INFINITY && - rinfo->metric < RIP_METRIC_INFINITY) + if (listcount (list) == 1) { - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; + if (newinfo.metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + else + rip_ecmp_delete (rinfo); } - - /* Update nexthop and/or metric value. */ - if (oldmetric != RIP_METRIC_INFINITY) + else { - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; - - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - } + if (newinfo.metric < rinfo->metric) + rip_ecmp_replace (&newinfo); + else if (newinfo.metric > rinfo->metric) + rip_ecmp_delete (rinfo); + else if (new_dist < old_dist) + rip_ecmp_replace (&newinfo); + else if (new_dist > old_dist) + rip_ecmp_delete (rinfo); + else + { + int update = CHECK_FLAG (rinfo->flags, RIP_RTF_FIB) ? 1 : 0; - /* - Set the route change flag and signal the output process - to trigger an update. */ - rinfo->flags |= RIP_RTF_CHANGED; - rip_event (RIP_TRIGGERED_UPDATE, 0); + assert (newinfo.metric != RIP_METRIC_INFINITY); - /* - If the new metric is infinity, start the deletion - process (described above); */ - if (rinfo->metric == RIP_METRIC_INFINITY) - { - /* If the new metric is infinity, the deletion process - begins for the route, which is no longer used for - routing packets. Note that the deletion process is - started only when the metric is first set to - infinity. If the metric was already infinity, then a - new deletion process is not started. */ - if (oldmetric != RIP_METRIC_INFINITY) - { - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, &newinfo, sizeof (struct rip_info)); + rip_timeout_update (rinfo); + + if (update) + rip_zebra_ipv4_add (rp); - /* - The metric for the route is set to 16 - (infinity). This causes the route to be removed - from service. */ - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rinfo->flags &= ~RIP_RTF_FIB; - - /* - The route change flag is to indicate that this - entry has been changed. */ - /* - The output process is signalled to trigger a - response. */ - ; /* Above processes are already done previously. */ + /* - Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + rip_event (RIP_TRIGGERED_UPDATE, 0); } } - else - { - /* otherwise, re-initialize the timeout. */ - rip_timeout_update (rinfo); - } } + else /* same & no change */ + rip_timeout_update (rinfo); + /* Unlock tempolary lock of the route. */ route_unlock_node (rp); } @@ -1519,8 +1580,9 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, unsigned int metric, unsigned char distance) { int ret; - struct route_node *rp; - struct rip_info *rinfo; + struct route_node *rp = NULL; + struct rip_info *rinfo = NULL, newinfo; + struct list *list = NULL; /* Redistribute route */ ret = rip_destination_check (p->prefix); @@ -1529,10 +1591,21 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, rp = route_node_get (rip->table, (struct prefix *) p); - rinfo = rp->info; + memset (&newinfo, 0, sizeof (struct rip_info)); + newinfo.type = type; + newinfo.sub_type = sub_type; + newinfo.ifindex = ifindex; + newinfo.metric = 1; + newinfo.external_metric = metric; + newinfo.distance = distance; + newinfo.rp = rp; + if (nexthop) + newinfo.nexthop = *nexthop; - if (rinfo) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIP_ROUTE_INTERFACE && rinfo->metric != RIP_METRIC_INFINITY) @@ -1554,35 +1627,11 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, } } - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop, - rinfo->metric); - rp->info = NULL; - rip_info_free (rinfo); - - route_unlock_node (rp); + rinfo = rip_ecmp_replace (&newinfo); + route_unlock_node (rp); } - - rinfo = rip_info_new (); - - rinfo->type = type; - rinfo->sub_type = sub_type; - rinfo->ifindex = ifindex; - rinfo->metric = 1; - rinfo->external_metric = metric; - rinfo->distance = distance; - rinfo->rp = rp; - - if (nexthop) - rinfo->nexthop = *nexthop; - - rinfo->flags |= RIP_RTF_FIB; - rp->info = rinfo; - - rinfo->flags |= RIP_RTF_CHANGED; + else + rinfo = rip_ecmp_add (&newinfo); if (IS_RIP_DEBUG_EVENT) { if (!nexthop) @@ -1595,7 +1644,6 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, ifindex2ifname(ifindex)); } - rip_event (RIP_TRIGGERED_UPDATE, 0); } @@ -1615,27 +1663,33 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, rp = route_node_lookup (rip->table, (struct prefix *) p); if (rp) { - rinfo = rp->info; + struct list *list = rp->info; - if (rinfo != NULL - && rinfo->type == type - && rinfo->sub_type == sub_type - && rinfo->ifindex == ifindex) - { - /* Perform poisoned reverse. */ - rinfo->metric = RIP_METRIC_INFINITY; - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); - RIP_TIMER_OFF (rinfo->t_timeout); - rinfo->flags |= RIP_RTF_CHANGED; + if (list != NULL && listcount (list) != 0) + { + rinfo = listgetdata (listhead (list)); + if (rinfo != NULL + && rinfo->type == type + && rinfo->sub_type == sub_type + && rinfo->ifindex == ifindex) + { + /* Perform poisoned reverse. */ + rinfo->metric = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + RIP_TIMER_OFF (rinfo->t_timeout); + rinfo->flags |= RIP_RTF_CHANGED; - if (IS_RIP_DEBUG_EVENT) - zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]", - inet_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex)); + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Poisone %s/%d on the interface %s with an " + "infinity metric [delete]", + inet_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(ifindex)); - rip_event (RIP_TRIGGERED_UPDATE, 0); - } + rip_event (RIP_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); } } @@ -1683,16 +1737,6 @@ rip_request_process (struct rip_packet *packet, int size, ntohs (rte->family) == 0 && ntohl (rte->metric) == RIP_METRIC_INFINITY) { - struct prefix_ipv4 saddr; - - /* saddr will be used for determining which routes to split-horizon. - Since the source address we'll pick will be on the same subnet as the - destination, for the purpose of split-horizoning, we'll - pretend that "from" is our source address. */ - saddr.family = AF_INET; - saddr.prefixlen = IPV4_MAX_BITLEN; - saddr.prefix = from->sin_addr; - /* All route with split horizon */ rip_output_process (ifc, from, rip_all_route, packet->version); } @@ -1717,7 +1761,7 @@ rip_request_process (struct rip_packet *packet, int size, rp = route_node_lookup (rip->table, (struct prefix *) &p); if (rp) { - rinfo = rp->info; + rinfo = listgetdata (listhead ((struct list *)rp->info)); rte->metric = htonl (rinfo->metric); route_unlock_node (rp); } @@ -2152,6 +2196,8 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, int num = 0; int rtemax; int subnetted = 0; + struct list *list = NULL; + struct listnode *listnode = NULL; /* Logging output event. */ if (IS_RIP_DEBUG_EVENT) @@ -2168,7 +2214,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* Reset stream and RTE counter. */ stream_reset (s); - rtemax = (RIP_PACKET_MAXSIZ - 4) / 20; + rtemax = RIP_MAX_RTE; /* Get RIP interface. */ ri = ifc->ifp->info; @@ -2209,8 +2255,9 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, } for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); /* For RIPv1, if we are subnetted, output subnets in our network */ /* that have the same mask as the output "interface". For other */ /* networks, only the classfull version is output. */ @@ -2269,11 +2316,22 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - continue; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + int suppress = 0; + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + { + suppress = 1; + break; + } + + if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) + suppress = 1; + + if (suppress) continue; } @@ -2367,12 +2425,15 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - rinfo->metric_out = RIP_METRIC_INFINITY; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + rinfo->metric_out = RIP_METRIC_INFINITY; + if (tmp_rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) - rinfo->metric_out = RIP_METRIC_INFINITY; + rinfo->metric_out = RIP_METRIC_INFINITY; } /* Prepare preamble, auth headers, if needs be */ @@ -2592,12 +2653,18 @@ static void rip_clear_changed_flag (void) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - if (rinfo->flags & RIP_RTF_CHANGED) - rinfo->flags &= ~RIP_RTF_CHANGED; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + UNSET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + /* This flag can be set only on the first entry. */ + break; + } } /* Triggered update interval timer. */ @@ -2662,14 +2729,16 @@ void rip_redistribute_withdraw (int type) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; if (!rip) return; for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL) { + rinfo = listgetdata (listhead (list)); if (rinfo->type == type && rinfo->sub_type != RIP_ROUTE_INTERFACE) { @@ -2773,7 +2842,7 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, } return sizeof (rip_packet); } - + static int rip_update_jitter (unsigned long time) { @@ -2827,7 +2896,7 @@ rip_event (enum rip_event event, int sock) break; } } - + DEFUN (router_rip, router_rip_cmd, "router rip", @@ -2982,12 +3051,15 @@ static void rip_update_default_metric (void) { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) - if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) - rinfo->metric = rip->default_metric; + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) + rinfo->metric = rip->default_metric; } #endif @@ -3103,7 +3175,7 @@ ALIAS (no_rip_timers, "Routing information timeout timer. Default is 180.\n" "Garbage collection timer. Default is 120.\n") - + struct route_table *rip_distance_table; struct rip_distance @@ -3180,7 +3252,6 @@ rip_distance_unset (struct vty *vty, const char *distance_str, { int ret; struct prefix_ipv4 p; - u_char distance; struct route_node *rn; struct rip_distance *rdistance; @@ -3191,8 +3262,6 @@ rip_distance_unset (struct vty *vty, const char *distance_str, return CMD_WARNING; } - distance = atoi (distance_str); - rn = route_node_lookup (rip_distance_table, (struct prefix *)&p); if (! rn) { @@ -3371,7 +3440,81 @@ DEFUN (no_rip_distance_source_access_list, rip_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + +/* Update ECMP routes to zebra when ECMP is disabled. */ +static void +rip_ecmp_disable (void) +{ + struct route_node *rp; + struct rip_info *rinfo, *tmp_rinfo; + struct list *list; + struct listnode *node, *nextnode; + + if (!rip) + return; + + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL && listcount (list) > 1) + { + rinfo = listgetdata (listhead (list)); + if (!rip_route_rte (rinfo)) + continue; + + /* Drop all other entries, except the first one. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + /* Update zebra. */ + rip_zebra_ipv4_add (rp); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + } +} + +DEFUN (rip_allow_ecmp, + rip_allow_ecmp_cmd, + "allow-ecmp", + "Allow Equal Cost MultiPath\n") +{ + if (rip->ecmp) + { + vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 1; + zlog_info ("ECMP is enabled."); + return CMD_SUCCESS; +} + +DEFUN (no_rip_allow_ecmp, + no_rip_allow_ecmp_cmd, + "no allow-ecmp", + NO_STR + "Allow Equal Cost MultiPath\n") +{ + if (!rip->ecmp) + { + vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 0; + zlog_info ("ECMP is disabled."); + rip_ecmp_disable (); + return CMD_SUCCESS; +} + /* Print out routes update time. */ static void rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo) @@ -3426,7 +3569,9 @@ DEFUN (show_ip_rip, "Show RIP routes\n") { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (! rip) return CMD_SUCCESS; @@ -3439,7 +3584,8 @@ DEFUN (show_ip_rip, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) { int len; @@ -3676,6 +3822,10 @@ config_write_rip (struct vty *vty) rdistance->access_list ? rdistance->access_list : "", VTY_NEWLINE); + /* ECMP configuration. */ + if (rip->ecmp) + vty_out (vty, " allow-ecmp%s", VTY_NEWLINE); + /* RIP static route configuration. */ for (rn = route_top (rip->route); rn; rn = route_next (rn)) if (rn->info) @@ -3695,7 +3845,7 @@ static struct cmd_node rip_node = "%s(config-router)# ", 1 }; - + /* Distribute-list update functions. */ static void rip_distribute_update (struct distribute *dist) @@ -3786,34 +3936,37 @@ rip_distribute_update_all_wrapper(struct access_list *notused) { rip_distribute_update_all(NULL); } - + /* Delete all added rip route. */ void rip_clean (void) { int i; struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (rip) { /* Clear RIP routes */ for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - { - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->sub_type == RIP_ROUTE_RTE) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - route_unlock_node (rp); - - rip_info_free (rinfo); - } + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (rip_route_rte (rinfo)) + rip_zebra_ipv4_delete (rp); + + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + rip_info_free (rinfo); + } + list_delete (list); + rp->info = NULL; + route_unlock_node (rp); + } /* Cancel RIP related timers. */ RIP_TIMER_OFF (rip->t_update); @@ -4010,6 +4163,8 @@ rip_init (void) install_element (RIP_NODE, &no_rip_distance_source_cmd); install_element (RIP_NODE, &rip_distance_source_access_list_cmd); install_element (RIP_NODE, &no_rip_distance_source_access_list_cmd); + install_element (RIP_NODE, &rip_allow_ecmp_cmd); + install_element (RIP_NODE, &no_rip_allow_ecmp_cmd); /* Debug related init. */ rip_debug_init (); diff --git a/ripd/ripd.h b/ripd/ripd.h index 45b07b9c..4f40e79a 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -49,7 +49,7 @@ #define RIP_RTE_SIZE 20 /* Max count of routing table entry in one rip packet. */ -#define RIP_MAX_RTE 25 +#define RIP_MAX_RTE ((RIP_PACKET_MAXSIZ - RIP_HEADER_SIZE) / RIP_RTE_SIZE) /* RIP version 2 multicast address. */ #ifndef INADDR_RIP_GROUP @@ -143,6 +143,9 @@ struct rip u_char distance; struct route_table *distance_table; + /* RIP ECMP flag */ + unsigned int ecmp; + /* For redistribute route map. */ struct { @@ -401,8 +404,8 @@ extern void rip_redistribute_add (int, int, struct prefix_ipv4 *, unsigned int, struct in_addr *, unsigned int, unsigned char); extern void rip_redistribute_delete (int, int, struct prefix_ipv4 *, unsigned int); extern void rip_redistribute_withdraw (int); -extern void rip_zebra_ipv4_add (struct prefix_ipv4 *, struct in_addr *, u_int32_t, u_char); -extern void rip_zebra_ipv4_delete (struct prefix_ipv4 *, struct in_addr *, u_int32_t); +extern void rip_zebra_ipv4_add (struct route_node *); +extern void rip_zebra_ipv4_delete (struct route_node *); extern void rip_interface_multicast_set (int, struct connected *); extern void rip_distribute_update_interface (struct interface *); extern void rip_if_rmap_update_interface (struct interface *); @@ -429,6 +432,10 @@ extern void rip_redistribute_clean (void); extern void rip_ifaddr_add (struct interface *, struct connected *); extern void rip_ifaddr_delete (struct interface *, struct connected *); +extern struct rip_info *rip_ecmp_add (struct rip_info *); +extern struct rip_info *rip_ecmp_replace (struct rip_info *); +extern struct rip_info *rip_ecmp_delete (struct rip_info *); + /* There is only one rip strucutre. */ extern struct rip *rip; diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am index de5bebae..9bd14ba0 100644 --- a/ripngd/Makefile.am +++ b/ripngd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c index 33a7cf69..c1eb39ba 100644 --- a/ripngd/ripng_debug.c +++ b/ripngd/ripng_debug.c @@ -28,7 +28,7 @@ unsigned long ripng_debug_event = 0; unsigned long ripng_debug_packet = 0; unsigned long ripng_debug_zebra = 0; - + DEFUN (show_debugging_ripng, show_debugging_ripng_cmd, "show debugging ripng", diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 6718fff2..8717bfb0 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -38,7 +38,7 @@ #include "ripngd/ripngd.h" #include "ripngd/ripng_debug.h" - + /* If RFC2133 definition is used. */ #ifndef IPV6_JOIN_GROUP #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP @@ -505,7 +505,7 @@ ripng_interface_address_delete (int command, struct zclient *zclient, return 0; } - + /* RIPng enable interface vector. */ vector ripng_enable_if; @@ -812,7 +812,7 @@ ripng_enable_apply_all (void) for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) ripng_enable_apply (ifp); } - + /* Clear all network and neighbor configuration */ void ripng_clean_network () @@ -835,7 +835,7 @@ ripng_clean_network () vector_slot (ripng_enable_if, i) = NULL; } } - + /* Vector to store passive-interface name. */ vector Vripng_passive_interface; @@ -1103,7 +1103,7 @@ DEFUN (no_ripng_passive_interface, { return ripng_passive_interface_unset (vty, argv[0]); } - + static struct ripng_interface * ri_new (void) { diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 20225b7d..acc980de 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -127,7 +127,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -182,7 +182,7 @@ struct quagga_signal_t ripng_signals[] = .handler = &sigint, }, }; - + /* RIPngd main routine. */ int main (int argc, char **argv) @@ -282,9 +282,6 @@ main (int argc, char **argv) zebra_init (); ripng_peer_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c index 05f190e2..b966af00 100644 --- a/ripngd/ripng_nexthop.c +++ b/ripngd/ripng_nexthop.c @@ -209,7 +209,6 @@ ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp, if (ret >= 0 && IS_RIPNG_DEBUG_SEND) ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s), stream_get_endp (s), "SEND"); - num = 0; stream_reset (s); } } diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index c04456b8..b12e1460 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -38,7 +38,7 @@ /* Linked list of RIPng peer. */ struct list *peer_list; - + static struct ripng_peer * ripng_peer_new (void) { diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 0f5cca35..f4fadb67 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -29,7 +29,7 @@ #include "sockunion.h" #include "ripngd/ripngd.h" - + struct rip_metric_modifier { enum @@ -42,7 +42,7 @@ struct rip_metric_modifier u_char metric; }; - + static int ripng_route_match_add (struct vty *vty, struct route_map_index *index, const char *command, const char *arg) @@ -130,7 +130,7 @@ ripng_route_set_delete (struct vty *vty, struct route_map_index *index, } return CMD_SUCCESS; } - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -184,7 +184,7 @@ static struct route_map_rule_cmd route_match_metric_cmd = route_match_metric_compile, route_match_metric_free }; - + /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -284,7 +284,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = route_match_tag_compile, route_match_tag_free }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -496,7 +496,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = route_set_tag_compile, route_set_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 8e766062..aa54bbed 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -41,7 +41,7 @@ int ripng_interface_add (int, struct zclient *, zebra_size_t); int ripng_interface_delete (int, struct zclient *, zebra_size_t); int ripng_interface_address_add (int, struct zclient *, zebra_size_t); int ripng_interface_address_delete (int, struct zclient *, zebra_size_t); - + void ripng_zebra_ipv6_add (struct prefix_ipv6 *p, struct in6_addr *nexthop, unsigned int ifindex, u_char metric) @@ -206,7 +206,7 @@ ripng_redistribute_routemap_unset (int type) ripng->route_map[type].name = NULL; ripng->route_map[type].map = NULL; } - + /* Redistribution types */ static struct { int type; @@ -216,9 +216,12 @@ static struct { {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, {ZEBRA_ROUTE_CONNECT, 1, "connected"}, {ZEBRA_ROUTE_STATIC, 1, "static"}, - {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, + {ZEBRA_ROUTE_OSPF6, 2, "ospf6"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, - {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, {0, 0, NULL} }; diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 8e97c2f8..8c20a7a2 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -95,7 +95,7 @@ ripng_info_free (struct ripng_info *rinfo) { XFREE (MTYPE_RIPNG_ROUTE, rinfo); } - + /* Create ripng socket. */ static int ripng_make_socket (void) @@ -717,7 +717,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* If offset-list does not modify the metric use interface's * one. */ if (! ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIPNG_METRIC_INFINITY) rte->metric = RIPNG_METRIC_INFINITY; @@ -928,15 +928,6 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* MUSICA or LINUX */ rp = route_node_get (ripng->table, (struct prefix *) p); rinfo = rp->info; @@ -1025,15 +1016,6 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* MUSICA or LINUX */ rp = route_node_lookup (ripng->table, (struct prefix *) p); @@ -1886,7 +1868,7 @@ ripng_request (struct interface *ifp) NULL, ifp); } - + static int ripng_update_jitter (int time) { @@ -1928,7 +1910,7 @@ ripng_event (enum ripng_event event, int sock) break; } } - + /* Print out routes update time. */ static void @@ -2767,7 +2749,7 @@ ripng_distribute_update_all_wrapper (struct access_list *notused) { ripng_distribute_update_all(NULL); } - + /* delete all the added ripng routes. */ void ripng_clean() diff --git a/solaris/quagga.init.in b/solaris/quagga.init.in index 00426241..ee3a987f 100755 --- a/solaris/quagga.init.in +++ b/solaris/quagga.init.in @@ -146,31 +146,6 @@ routeadm_daemon_args () { echo ${args} } -# certain daemons need zebra -routeadm_zebra_enable () { - - if [ "$DAEMON" = "zebra" ]; then - return - fi - - enable_zebra=`/usr/bin/svcprop -p \ - routing/enable_zebra $SMF_FMRI 2> /dev/null` - if [ "$enable_zebra" != "false" ]; then - zenabled=`/usr/bin/svcprop -p general/enabled zebra:quagga` - zenabledt=`/usr/bin/svcprop -p general_ovr/enabled zebra:quagga` - if [ "$zenabled" = "true" -o "$zenabledt" = "true" ]; then - /usr/sbin/svcadm disable zebra:quagga - /usr/sbin/svcadm enable -st zebra:quagga - else - /usr/sbin/svcadm enable -st zebra:quagga - fi - if [ "$?" != "0" ]; then - echo "Could not enable zebra:quagga" - exit $SMF_EXIT_ERR_FATAL - fi - fi -} - # Include smf functions, if available. If not, define smf_present to indicate # there is no SMF. Should allow this script to work pre-S10. if [ -f "$SMFINCLUDE" ] ; then @@ -247,7 +222,6 @@ esac if [ smf_present -a -f "$ROUTEADMINCLUDE" ]; then handle_routeadm_upgrade $DAEMON; DAEMON_ARGS=`routeadm_daemon_args`; - routeadm_zebra_enable $DAEMON; else if [ $# -gt 0 ] ; then shift diff --git a/solaris/quagga.xml.in b/solaris/quagga.xml.in index 50c52c22..60427b06 100644 --- a/solaris/quagga.xml.in +++ b/solaris/quagga.xml.in @@ -21,6 +21,8 @@ Copyright 2007 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. + Copyright 2015 Joyent, Inc. + ident "@(#)quagga.xml 1.0 05/03/15 SMI" --> @@ -189,7 +191,7 @@ @@ -320,7 +322,7 @@ @@ -449,7 +451,7 @@ @@ -580,7 +582,7 @@ @@ -715,7 +717,7 @@ diff --git a/tests/.gitignore b/tests/.gitignore index e6ab706c..1cb28c9f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,16 +5,36 @@ tags TAGS .deps .nfs* +*~ +*.loT *.lo *.la *.libs -tabletest -testsig +*.bak +*.log +*.sum +*.xml .arch-inventory .arch-ids +aspathtest +ecommtest +heavy +heavythread +heavywq +tabletest +test-timer-correctness +test-timer-performance +testbgpcap +testbgpmpath +testbgpmpattr testbuffer +testchecksum testmemory +testprivs +testsegv testsig -*~ -*.loT - +teststream +testnexthopiter +testcommands +test-commands-defun.c +site.exp diff --git a/tests/Makefile.am b/tests/Makefile.am index cd6b2f1b..1fe28c70 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,14 +1,52 @@ -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AUTOMAKE_OPTIONS = dejagnu +DEJATOOL = libzebra + +SUBDIRS = \ + bgpd.tests \ + libzebra.tests + +EXTRA_DIST = \ + config/unix.exp \ + lib/bgpd.exp \ + lib/libzebra.exp \ + global-conf.exp \ + testcommands.in \ + testcommands.refout + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" AM_CFLAGS = $(PICFLAGS) AM_LDFLAGS = $(PILDFLAGS) -noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - aspathtest testprivs teststream testbgpcap ecommtest \ - testbgpmpattr testchecksum testbgpmpath tabletest +if BGPD +TESTS_BGPD = aspathtest testbgpcap ecommtest testbgpmpattr testbgpmpath +DEJATOOL += bgpd +else +TESTS_BGPD = +endif + +check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \ + testprivs teststream testchecksum tabletest testnexthopiter \ + testcommands test-timer-correctness test-timer-performance \ + $(TESTS_BGPD) + +../vtysh/vtysh_cmd.c: + $(MAKE) -C ../vtysh vtysh_cmd.c + +test-commands-defun.c: ../vtysh/vtysh_cmd.c + sed \ + -e '/"vtysh.h"/d' \ + -e 's/vtysh_init_cmd/test_init_cmd/' \ + -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ + < ../vtysh/vtysh_cmd.c \ + > test-commands-defun.c + +BUILT_SOURCES = test-commands-defun.c +noinst_HEADERS = prng.h testsig_SOURCES = test-sig.c +testsegv_SOURCES = test-segv.c testbuffer_SOURCES = test-buffer.c testmemory_SOURCES = test-memory.c testprivs_SOURCES = test-privs.c @@ -23,8 +61,13 @@ testbgpmpattr_SOURCES = bgp_mp_attr_test.c testchecksum_SOURCES = test-checksum.c testbgpmpath_SOURCES = bgp_mpath_test.c tabletest_SOURCES = table_test.c +testnexthopiter_SOURCES = test-nexthop-iter.c prng.c +testcommands_SOURCES = test-commands-defun.c test-commands.c prng.c +test_timer_correctness_SOURCES = test-timer-correctness.c prng.c +test_timer_performance_SOURCES = test-timer-performance.c prng.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ +testsegv_LDADD = ../lib/libzebra.la @LIBCAP@ testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@ testmemory_LDADD = ../lib/libzebra.la @LIBCAP@ testprivs_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -39,3 +82,7 @@ testbgpmpattr_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm +testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@ +testcommands_LDADD = ../lib/libzebra.la @LIBCAP@ +test_timer_correctness_LDADD = ../lib/libzebra.la @LIBCAP@ +test_timer_performance_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 9170455f..0aa3e47e 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2005 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" @@ -432,6 +453,16 @@ static struct test_segment { }, { NULL, NULL, {0}, 0, { NULL, 0, 0 } } }; +#define COMMON_ATTRS \ + BGP_ATTR_FLAG_TRANS, \ + BGP_ATTR_ORIGIN, \ + 1, \ + BGP_ORIGIN_EGP, \ + BGP_ATTR_FLAG_TRANS, \ + BGP_ATTR_NEXT_HOP, \ + 4, 192, 0, 2, 0 +#define COMMON_ATTR_SIZE 11 + /* */ static struct aspath_tests { const char *desc; @@ -443,6 +474,7 @@ static struct aspath_tests { const int cap; /* capabilities to set for peer */ const char attrheader [1024]; size_t len; + const struct test_segment *old_segment; } aspath_tests [] = { /* 0 */ @@ -452,11 +484,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, 0, 0, - { BGP_ATTR_FLAG_TRANS, - BGP_ATTR_AS_PATH, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 1 */ { @@ -465,11 +498,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 8, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 2 */ { @@ -478,11 +512,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 12, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 3 */ { @@ -491,11 +526,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 4 */ { @@ -504,11 +540,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 5 */ { @@ -517,11 +554,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 6 */ { @@ -530,11 +568,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, 0, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 18, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 7 */ { @@ -543,11 +582,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 16, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 8 */ { @@ -556,11 +596,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 20, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 9 */ { @@ -569,11 +610,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 22, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 10 */ { @@ -582,24 +624,41 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS_PATH, 18, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 11 */ { - "4b AS_PATH: confed", + "4b AS4_PATH w/o AS_PATH", &test_segments[6], - "8466 3 52737 4096", + NULL, AS4_DATA, -1, PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 14, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 12 */ + { + "4b AS4_PATH: confed", + &test_segments[6], + "8466 3 52737 4096 (123 456 789)", + AS4_DATA, 0, + PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 14, }, - 3, + COMMON_ATTR_SIZE + 3, + &test_segments[0], }, { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 }, }; @@ -1212,6 +1271,14 @@ handle_attr_test (struct aspath_tests *t) stream_write (peer.ibuf, t->attrheader, t->len); datalen = aspath_put (peer.ibuf, asp, t->as4 == AS4_DATA); + if (t->old_segment) + { + char dummyaspath[] = { BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, + t->old_segment->len }; + stream_write (peer.ibuf, dummyaspath, sizeof (dummyaspath)); + stream_write (peer.ibuf, t->old_segment->asdata, t->old_segment->len); + datalen += sizeof (dummyaspath) + t->old_segment->len; + } ret = bgp_attr_parse (&peer, &attr, t->len + datalen, NULL, NULL); @@ -1224,12 +1291,12 @@ handle_attr_test (struct aspath_tests *t) if (ret != 0) goto out; - if (attr.aspath == NULL) + if (t->shouldbe && attr.aspath == NULL) { - printf ("aspath is NULL!\n"); + printf ("aspath is NULL, but should be: %s\n", t->shouldbe); failed++; } - if (attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) + if (t->shouldbe && attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) { printf ("attr str and 'shouldbe' mismatched!\n" "attr str: %s\n" @@ -1237,6 +1304,11 @@ handle_attr_test (struct aspath_tests *t) attr.aspath->str, t->shouldbe); failed++; } + if (!t->shouldbe && attr.aspath) + { + printf ("aspath should be NULL, but is: %s\n", attr.aspath->str); + failed++; + } out: if (attr.aspath) diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 65c6a700..96f18f01 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2007 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index f086740f..5f0e733d 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" @@ -436,8 +457,8 @@ parse_test (struct peer *peer, struct test_segment *t, int type) { int ret; int oldfailed = failed; - struct attr attr; - struct bgp_nlri nlri; + struct attr attr = { }; + struct bgp_nlri nlri = { }; struct bgp_attr_parser_args attr_args = { .peer = peer, .length = t->len, @@ -478,7 +499,7 @@ parse_test (struct peer *peer, struct test_segment *t, int type) printf ("parsed?: %s\n", ret ? "no" : "yes"); - if (ret != t->parses) + if ((ret == 0) != (t->parses == 0)) failed++; if (tty) diff --git a/tests/bgpd.tests/Makefile.am b/tests/bgpd.tests/Makefile.am new file mode 100644 index 00000000..5900186c --- /dev/null +++ b/tests/bgpd.tests/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = \ + aspathtest.exp \ + ecommtest.exp \ + testbgpcap.exp \ + testbgpmpath.exp \ + testbgpmpattr.exp + diff --git a/tests/bgpd.tests/aspathtest.exp b/tests/bgpd.tests/aspathtest.exp new file mode 100644 index 00000000..dfecec78 --- /dev/null +++ b/tests/bgpd.tests/aspathtest.exp @@ -0,0 +1,76 @@ +set timeout 10 +set testprefix "aspathtest " +set aborted 0 +set color 1 + +spawn "./aspathtest" + +# proc onetest { test_name note start } { +# proc headerline { line } { + +set parserno 0 +proc parsertest { test_name } { + global parserno + headerline "test $parserno" + onetest "parse $test_name" " ($parserno)" "$test_name:" + onetest "parse $test_name +empty_prepend" " (#$parserno)" "empty prepend $test_name:" + incr parserno 1 +} +set attrno 0 +proc attrtest { test_name } { + global attrno + headerline "aspath_attr test $attrno" + onetest "attr $test_name" " (#$attrno)" "$test_name" + incr attrno 1 +} + + +parsertest "seq1" +parsertest "seq2" +parsertest "seq3" +parsertest "seqset" +parsertest "seqset2" +parsertest "multi" +parsertest "confed" +parsertest "confed2" +parsertest "confset" +parsertest "confmulti" +parsertest "seq4" +parsertest "tripleseq1" +parsertest "someprivate" +parsertest "allprivate" +parsertest "long" +parsertest "seq1extra" +parsertest "empty" +parsertest "redundantset" +parsertest "reconcile_lead_asp" +parsertest "reconcile_new_asp" +parsertest "reconcile_confed" +parsertest "reconcile_start_trans" +parsertest "reconcile_start_trans4" +parsertest "reconcile_start_trans_error" +parsertest "redundantset2" +parsertest "zero-size overflow" +parsertest "zero-size overflow + valid segment" +parsertest "invalid segment type" + +for {set i 0} {$i < 10} {incr i 1} { onetest "prepend $i" "" "prepend test $i"; } +for {set i 0} {$i < 5} {incr i 1} { onetest "aggregate $i" "" "aggregate test $i"; } +for {set i 0} {$i < 5} {incr i 1} { onetest "reconcile $i" "" "reconcile test $i"; } +for {set i 0} {$i < 22} {incr i 1} { onetest "compare $i" "" "left cmp "; } + +onetest "empty_get" "" "empty_get_test" +attrtest "basic test" +attrtest "length too short" +attrtest "length too long" +attrtest "incorrect flag" +attrtest "as4_path, with as2 format data" +attrtest "as4, with incorrect attr length" +attrtest "basic 4-byte as-path" +attrtest "4b AS_PATH: too short" +attrtest "4b AS_PATH: too long" +attrtest "4b AS_PATH: too long2" +attrtest "4b AS_PATH: bad flags" +attrtest "4b AS4_PATH w/o AS_PATH" +attrtest "4b AS4_PATH: confed" + diff --git a/tests/bgpd.tests/ecommtest.exp b/tests/bgpd.tests/ecommtest.exp new file mode 100644 index 00000000..074952fa --- /dev/null +++ b/tests/bgpd.tests/ecommtest.exp @@ -0,0 +1,13 @@ +set timeout 10 +set testprefix "ecommtest " +set aborted 0 +set color 0 + +spawn "./ecommtest" + +# proc simpletest { start } { + +simpletest "ipaddr" +simpletest "ipaddr-so" +simpletest "asn" +simpletest "asn4" diff --git a/tests/bgpd.tests/testbgpcap.exp b/tests/bgpd.tests/testbgpcap.exp new file mode 100644 index 00000000..2572623f --- /dev/null +++ b/tests/bgpd.tests/testbgpcap.exp @@ -0,0 +1,51 @@ +set timeout 10 +set testprefix "testbgpcap " +set aborted 0 +set color 1 + +spawn "./testbgpcap" + +# proc simpletest { start } { + +simpletest "MP4: MP IP/Uni" +simpletest "MPv6: MP IPv6/Uni" +simpletest "MP2: MP IP/Multicast" +simpletest "MP3: MP IP6/MPLS-labeled VPN" +simpletest "MP5: MP IP6/MPLS-VPN" +simpletest "MP6: MP IP4/MPLS-laveled VPN" +simpletest "MP8: MP unknown AFI/SAFI" +simpletest "MP-short: MP IP4/Unicast, length too short (< minimum)" +simpletest "MP-overflow: MP IP4/Unicast, length too long" +simpletest "caphdr: capability header, and no more" +simpletest "nodata: header, no data but length says there is" +simpletest "padded: valid, with padding" +simpletest "minsize: violates minsize requirement" +simpletest "ORF: ORF, simple, single entry, single tuple" +simpletest "ORF-many: ORF, multi entry/tuple" +simpletest "ORFlo: ORF, multi entry/tuple, hdr length too short" +simpletest "ORFlu: ORF, multi entry/tuple, length too long" +simpletest "ORFnu: ORF, multi entry/tuple, entry number too long" +simpletest "ORFno: ORF, multi entry/tuple, entry number too short" +simpletest "ORFpad: ORF, multi entry/tuple, padded to align" +simpletest "AS4: AS4 capability" +simpletest "GR: GR capability" +simpletest "GR-short: GR capability, but header length too short" +simpletest "GR-long: GR capability, but header length too long" +simpletest "GR-trunc: GR capability, but truncated" +simpletest "GR-empty: GR capability, but empty." +simpletest "MP-empty: MP capability, but empty." +simpletest "ORF-empty: ORF capability, but empty." +simpletest "AS4-empty: AS4 capability, but empty." +simpletest "dyn-empty: Dynamic capability, but empty." +simpletest "dyn-old: Dynamic capability (deprecated version)" +simpletest "Cap-singlets: One capability per Optional-Param" +simpletest "Cap-series: Series of capability, one Optional-Param" +simpletest "AS4more: AS4 capability after other caps (singlets)" +simpletest "AS4series: AS4 capability, in series of capabilities" +simpletest "AS4real: AS4 capability, in series of capabilities" +simpletest "AS4real2: AS4 capability, in series of capabilities" +simpletest "DynCap: Dynamic Capability Message, IP/Multicast" +simpletest "DynCapLong: Dynamic Capability Message, IP/Multicast, truncated" +simpletest "DynCapPadded: Dynamic Capability Message, IP/Multicast, padded" +simpletest "DynCapMPCpadded: Dynamic Capability Message, IP/Multicast, cap data padded" +simpletest "DynCapMPCoverflow: Dynamic Capability Message, IP/Multicast, cap data != length" diff --git a/tests/bgpd.tests/testbgpmpath.exp b/tests/bgpd.tests/testbgpmpath.exp new file mode 100644 index 00000000..96a51e39 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpath.exp @@ -0,0 +1,12 @@ +set timeout 10 +set testprefix "testbgpmpath " +set aborted 0 +set color 1 + +spawn "./testbgpmpath" + +# proc simpletest { start } { + +simpletest "bgp maximum-paths config" +simpletest "bgp_mp_list" +simpletest "bgp_info_mpath_update" diff --git a/tests/bgpd.tests/testbgpmpattr.exp b/tests/bgpd.tests/testbgpmpattr.exp new file mode 100644 index 00000000..646bbe50 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpattr.exp @@ -0,0 +1,31 @@ +set timeout 10 +set testprefix "testbgpmpattr " +set aborted 0 +set color 1 + +spawn "./testbgpmpattr" + +# proc simpletest { start } { + +simpletest "IPv6: IPV6 MP Reach, global nexthop, 1 NLRI" +simpletest "IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs" +simpletest "IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default" +simpletest "IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default" +simpletest "IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length" +simpletest "IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length" +simpletest "IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow" +simpletest "IPv6-nhlen4: IPV6 MP Reach, nexthop length short" +simpletest "IPv6-nlri: IPV6 MP Reach, NLRI bitlen overflow" +simpletest "IPv4: IPv4 MP Reach, 2 NLRIs + default" +simpletest "IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow" +simpletest "IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow" +simpletest "IPv4-MLVPN: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, 3 NLRIs" +simpletest "IPv6-bug: IPv6, global nexthop, 1 default NLRI" +simpletest "IPv6-unreach: IPV6 MP Unreach, 1 NLRI" +simpletest "IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs" +simpletest "IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default" +simpletest "IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow" +simpletest "IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default" +simpletest "IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow" +simpletest "IPv4-unreach-MLVPN: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs" + diff --git a/tests/config/unix.exp b/tests/config/unix.exp new file mode 100644 index 00000000..2f6bcead --- /dev/null +++ b/tests/config/unix.exp @@ -0,0 +1,102 @@ + +# every test should always be run and always return some status. +# so, if we lose sync with a multi-test program, aborted will be used +# to flag the remainder of the tests as untested. +#set aborted 0 + +# only match with color codes since "failed" / "OK" might otherwise +# be part of the output... +#set color 1 + +set xfail 0 + +proc onesimple { test_name match } { + global verbose + global aborted + global testprefix + if { $aborted > 0 } { + untested "$testprefix$test_name" + return + } + if { $verbose > 0 } { + send_user "$testprefix$test_name$note\n" + } + expect { + "$match" { pass "$testprefix$test_name"; } + eof { fail "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } +} + +proc onetest { test_name note start } { + global aborted + global testprefix + global verbose + global color + global xfail + + if { $aborted > 0 } { + untested "$testprefix$test_name" + return + } + + if { $verbose > 0 } { + send_user "$testprefix$test_name$note\n" + } + expect { + "$start" { } + + eof { unresolved "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } + + if { $aborted > 0 } { + send_user "sync failed: $testprefix$test_name$note -- $testprefix aborted!\n" + return + } + + if { $color } { + set pat "(32mOK|31mfailed)" + } else { + set pat "(OK|failed)" + } + expect { + # need this because otherwise expect will skip over a "failed" and + # grab the next "OK" (or the other way around) + -re "$pat" { + if { "$expect_out(0,string)" == "32mOK" || "$expect_out(0,string)" == "OK" } { + pass "$testprefix$test_name" + } else { + if { $xfail } { + xfail "$testprefix$test_name" + } else { + fail "$testprefix$test_name" + } + } + return + } + + eof { unresolved "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } + + if { $aborted > 0 } { + send_user "failed: $testprefix$test_name$note -- $testprefix aborted!\n" + return + } +} + +proc headerline { line } { + global aborted + if { $aborted > 0 } { return; } + expect { + $line { return; } + eof { send_user "numbering mismatch!\n"; set aborted 1; } + timeout { send_user "numbering mismatch!\n"; set aborted 1; } + } +} + +proc simpletest { start } { + onetest "$start" "" "$start" +} + diff --git a/tests/ecommunity_test.c b/tests/ecommunity_test.c index 87f20f28..cd1681d5 100644 --- a/tests/ecommunity_test.c +++ b/tests/ecommunity_test.c @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2007 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ #include #include "vty.h" diff --git a/tests/global-conf.exp b/tests/global-conf.exp new file mode 100644 index 00000000..e69de29b diff --git a/tests/lib/bgpd.exp b/tests/lib/bgpd.exp new file mode 100644 index 00000000..e69de29b diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp new file mode 100644 index 00000000..e69de29b diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am new file mode 100644 index 00000000..819cce2f --- /dev/null +++ b/tests/libzebra.tests/Makefile.am @@ -0,0 +1,5 @@ +EXTRA_DIST = \ + tabletest.exp \ + test-timer-correctness.exp \ + testcommands.exp \ + testnexthopiter.exp diff --git a/tests/libzebra.tests/tabletest.exp b/tests/libzebra.tests/tabletest.exp new file mode 100644 index 00000000..5838d4fc --- /dev/null +++ b/tests/libzebra.tests/tabletest.exp @@ -0,0 +1,9 @@ +set timeout 10 +set testprefix "tabletest " +set aborted 0 + +spawn "./tabletest" + +for {set i 0} {$i < 6} {incr i 1} { onesimple "cmp $i" "Verifying cmp"; } +for {set i 0} {$i < 11} {incr i 1} { onesimple "succ $i" "Verifying successor"; } +onesimple "pause" "Verified pausing" diff --git a/tests/libzebra.tests/test-timer-correctness.exp b/tests/libzebra.tests/test-timer-correctness.exp new file mode 100644 index 00000000..83531c7d --- /dev/null +++ b/tests/libzebra.tests/test-timer-correctness.exp @@ -0,0 +1,7 @@ +set timeout 10 +set testprefix "test-timer-correctness" +set aborted 0 + +spawn "./test-timer-correctness" + +onesimple "" "Expected output and actual output match." diff --git a/tests/libzebra.tests/testcommands.exp b/tests/libzebra.tests/testcommands.exp new file mode 100644 index 00000000..d4bfc823 --- /dev/null +++ b/tests/libzebra.tests/testcommands.exp @@ -0,0 +1,31 @@ +set timeout 30 +set test_name "testcommands" + +if {![info exists env(QUAGGA_TEST_COMMANDS)]} { + # sadly, the test randomly fails when configure parameters differ from + # what was used to create testcommands.refout. this can be fixed by + # shipping a matching vtysh_cmd.c, which we'll add after 0.99.23 + unresolved "$test_name" + exit 0 +} + +spawn sh -c "./testcommands -e 0 < $env(srcdir)/testcommands.in | diff -au - $env(srcdir)/testcommands.refout" + +expect { + eof { + } + timeout { + exp_close + fail "$test_name: timeout" + } +} + +catch wait result +set os_error [lindex $result 2] +set exit_status [lindex $result 3] + +if { $os_error == 0 && $exit_status == 0 } { + pass "$test_name" +} else { + fail "$test_name" +} diff --git a/tests/libzebra.tests/testnexthopiter.exp b/tests/libzebra.tests/testnexthopiter.exp new file mode 100644 index 00000000..be35a0a2 --- /dev/null +++ b/tests/libzebra.tests/testnexthopiter.exp @@ -0,0 +1,8 @@ +set timeout 10 +set testprefix "testnexthopiter " +set aborted 0 + +spawn "./testnexthopiter" + +onesimple "simple" "Simple test passed." +onesimple "prng" "PRNG test passed." diff --git a/tests/libzebra.tests/teststream.exp b/tests/libzebra.tests/teststream.exp new file mode 100644 index 00000000..ca602e30 --- /dev/null +++ b/tests/libzebra.tests/teststream.exp @@ -0,0 +1,28 @@ +set timeout 10 +spawn "./teststream" + +expect { + "endp: 15, readable: 15, writeable: 1009" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "endp: 15, readable: 15, writeable: 0" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "c: 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "w: 0xbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "l: 0xdeadbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "q: 0xdeadbeefdeadbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +pass "teststream" diff --git a/tests/main.c b/tests/main.c index e0fbb4d5..5e7bdcb1 100644 --- a/tests/main.c +++ b/tests/main.c @@ -95,8 +95,8 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - - + + /* main routine. */ int main (int argc, char **argv) @@ -171,8 +171,6 @@ main (int argc, char **argv) /* OSPF vty inits. */ test_vty_init (); - sort_node (); - /* Change to the daemon program. */ if (daemon_mode && daemon (0, 0) < 0) { diff --git a/tests/prng.c b/tests/prng.c new file mode 100644 index 00000000..8d78ea52 --- /dev/null +++ b/tests/prng.c @@ -0,0 +1,130 @@ +/* + * Very simple prng to allow for randomized tests with reproducable + * results. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include + +#include "prng.h" + +struct prng +{ + unsigned long long state1; + unsigned long long state2; +}; + +static char +prng_bit(struct prng *prng) +{ + prng->state1 *= 2416; + prng->state1 += 374441; + prng->state1 %= 1771875; + + if (prng->state1 % 2) + { + prng->state2 *= 84589; + prng->state2 += 45989; + prng->state2 %= 217728; + } + + return prng->state2 % 2; +} + +struct prng* +prng_new(unsigned long long seed) +{ + struct prng *rv = calloc(sizeof(*rv), 1); + assert(rv); + + rv->state1 = rv->state2 = seed; + + return rv; +} + +unsigned int +prng_rand(struct prng *prng) +{ + unsigned int i, rv = 0; + + for (i = 0; i < 32; i++) + { + rv |= prng_bit(prng); + rv <<= 1; + } + return rv; +} + +const char * +prng_fuzz(struct prng *prng, + const char *string, + const char *charset, + unsigned int operations) +{ + static char buf[256]; + unsigned int charset_len; + unsigned int i; + unsigned int offset; + unsigned int op; + unsigned int character; + + assert(strlen(string) < sizeof(buf)); + + strncpy(buf, string, sizeof(buf)); + charset_len = strlen(charset); + + for (i = 0; i < operations; i++) + { + offset = prng_rand(prng) % strlen(buf); + op = prng_rand(prng) % 3; + + switch (op) + { + case 0: + /* replace */ + character = prng_rand(prng) % charset_len; + buf[offset] = charset[character]; + break; + case 1: + /* remove */ + memmove(buf + offset, buf + offset + 1, strlen(buf) - offset); + break; + case 2: + /* insert */ + assert(strlen(buf) + 1 < sizeof(buf)); + + memmove(buf + offset + 1, buf + offset, strlen(buf) + 1 - offset); + character = prng_rand(prng) % charset_len; + buf[offset] = charset[character]; + break; + } + } + return buf; +} + +void +prng_free(struct prng *prng) +{ + free(prng); +} diff --git a/tests/prng.h b/tests/prng.h new file mode 100644 index 00000000..cf0bacc5 --- /dev/null +++ b/tests/prng.h @@ -0,0 +1,38 @@ +/* + * Very simple prng to allow for randomized tests with reproducable + * results. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef _PRNG_H +#define _PRNG_H + +struct prng; + +struct prng* prng_new(unsigned long long seed); +unsigned int prng_rand(struct prng*); +const char * prng_fuzz(struct prng*, + const char *string, + const char *charset, + unsigned int operations); +void prng_free(struct prng *); + +#endif diff --git a/tests/test-buffer.c b/tests/test-buffer.c index b310776f..e95d6fb8 100644 --- a/tests/test-buffer.c +++ b/tests/test-buffer.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2004 Paul Jakma + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include diff --git a/tests/test-checksum.c b/tests/test-checksum.c index fc4eb02d..9672e95a 100644 --- a/tests/test-checksum.c +++ b/tests/test-checksum.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include @@ -24,7 +45,7 @@ typedef uint16_t testoff_t; /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102 - + /* Accumulator phase of checksum */ static struct acc_vals @@ -263,7 +284,7 @@ struct reductions_t { { .name = "isisd-mody", .f = reduce_isisd_mody }, { NULL, NULL }, }; - + /* The original ospfd checksum */ static u_int16_t ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off) diff --git a/tests/test-commands.c b/tests/test-commands.c new file mode 100644 index 00000000..18b3b50d --- /dev/null +++ b/tests/test-commands.c @@ -0,0 +1,415 @@ +/* + * Test code for lib/command.c + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This program reads in a list of commandlines from stdin + * and calls all the public functions of lib/command.c for + * both the given command lines and fuzzed versions thereof. + * + * The output is currently not validated but only logged. It can + * be diffed to find regressions between versions. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#define REALLY_NEED_PLAIN_GETOPT 1 + +#include + +#include +#include +#include + +#include "command.h" +#include "memory.h" +#include "vector.h" +#include "prng.h" + +extern vector cmdvec; +extern struct cmd_node vty_node; +extern void test_init_cmd(void); /* provided in test-commands-defun.c */ + +struct thread_master *master; /* dummy for libzebra*/ + +static vector test_cmds; +static char test_buf[32768]; + +static struct cmd_node bgp_node = +{ + BGP_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node rip_node = +{ + RIP_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node isis_node = +{ + ISIS_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", +}; + +static struct cmd_node rmap_node = +{ + RMAP_NODE, + "%s(config-route-map)# " +}; + +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "%s(config-router)# " +}; + +static struct cmd_node bgp_vpnv4_node = +{ + BGP_VPNV4_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv4_node = +{ + BGP_IPV4_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv4m_node = +{ + BGP_IPV4M_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv6_node = +{ + BGP_IPV6_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv6m_node = +{ + BGP_IPV6M_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node ospf_node = +{ + OSPF_NODE, + "%s(config-router)# " +}; + +static struct cmd_node ripng_node = +{ + RIPNG_NODE, + "%s(config-router)# " +}; + +static struct cmd_node ospf6_node = +{ + OSPF6_NODE, + "%s(config-ospf6)# " +}; + +static struct cmd_node babel_node = +{ + BABEL_NODE, + "%s(config-babel)# " +}; + +static struct cmd_node keychain_node = +{ + KEYCHAIN_NODE, + "%s(config-keychain)# " +}; + +static struct cmd_node keychain_key_node = +{ + KEYCHAIN_KEY_NODE, + "%s(config-keychain-key)# " +}; + +static int +test_callback(struct cmd_element *cmd, struct vty *vty, int argc, const char *argv[]) +{ + int offset; + int rv; + int i; + + offset = 0; + rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string); + if (rv < 0) + abort(); + + offset += rv; + + for (i = 0; i < argc; i++) + { + rv = snprintf(test_buf + offset, sizeof(test_buf) - offset, "%s'%s'", + (i == 0) ? ": " : ", ", argv[i]); + if (rv < 0) + abort(); + offset += rv; + } + + return CMD_SUCCESS; +} + +static void +test_load(void) +{ + char line[4096]; + + test_cmds = vector_init(VECTOR_MIN_SIZE); + + while (fgets(line, sizeof(line), stdin) != NULL) + { + if (strlen(line)) + line[strlen(line) - 1] = '\0'; + if (line[0] == '#') + continue; + vector_set(test_cmds, XSTRDUP(MTYPE_STRVEC, line)); + } +} + +static void +test_init(void) +{ + unsigned int node; + unsigned int i; + struct cmd_node *cnode; + struct cmd_element *cmd; + + cmd_init(1); + + install_node (&bgp_node, NULL); + install_node (&rip_node, NULL); + install_node (&interface_node, NULL); + install_node (&rmap_node, NULL); + install_node (&zebra_node, NULL); + install_node (&bgp_vpnv4_node, NULL); + install_node (&bgp_ipv4_node, NULL); + install_node (&bgp_ipv4m_node, NULL); + install_node (&bgp_ipv6_node, NULL); + install_node (&bgp_ipv6m_node, NULL); + install_node (&ospf_node, NULL); + install_node (&ripng_node, NULL); + install_node (&ospf6_node, NULL); + install_node (&babel_node, NULL); + install_node (&keychain_node, NULL); + install_node (&keychain_key_node, NULL); + install_node (&isis_node, NULL); + install_node (&vty_node, NULL); + + test_init_cmd(); + + for (node = 0; node < vector_active(cmdvec); node++) + if ((cnode = vector_slot(cmdvec, node)) != NULL) + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL) + { + cmd->daemon = 0; + cmd->func = test_callback; + } + test_load(); + vty_init_vtysh(); +} + +static void +test_terminate(void) +{ + unsigned int i; + + vty_terminate(); + for (i = 0; i < vector_active(test_cmds); i++) + XFREE(MTYPE_STRVEC, vector_slot(test_cmds, i)); + vector_free(test_cmds); + cmd_terminate(); +} + +static void +test_run(struct prng *prng, struct vty *vty, const char *cmd, unsigned int edit_dist, unsigned int node_index, int verbose) +{ + const char *test_str; + vector vline; + int ret; + unsigned int i; + char **completions; + unsigned int j; + struct cmd_node *cnode; + vector descriptions; + int appended_null; + int no_match; + + test_str = prng_fuzz(prng, cmd, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /", edit_dist); + vline = cmd_make_strvec(test_str); + + if (vline == NULL) + return; + + appended_null = 0; + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) + { + if (node_index != (unsigned int)-1 && i != node_index) + continue; + + if (appended_null) + { + vector_unset(vline, vector_active(vline) - 1); + appended_null = 0; + } + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command(vline, vty, NULL, 0); + no_match = (ret == CMD_ERR_NO_MATCH); + if (verbose || !no_match) + printf("execute relaxed '%s'@%d: rv==%d%s%s\n", + test_str, + cnode->node, + ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command_strict(vline, vty, NULL); + if (verbose || !no_match) + printf("execute strict '%s'@%d: rv==%d%s%s\n", + test_str, + cnode->node, + ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + if (isspace((int) test_str[strlen(test_str) - 1])) + { + vector_set (vline, NULL); + appended_null = 1; + } + + vty->node = cnode->node; + completions = cmd_complete_command(vline, vty, &ret); + if (verbose || !no_match) + printf("complete '%s'@%d: rv==%d\n", + test_str, + cnode->node, + ret); + if (completions != NULL) + { + for (j = 0; completions[j] != NULL; j++) + { + printf(" '%s'\n", completions[j]); + XFREE(MTYPE_TMP, completions[j]); + } + XFREE(MTYPE_VECTOR_INDEX, completions); + } + + vty->node = cnode->node; + descriptions = cmd_describe_command(vline, vty, &ret); + if (verbose || !no_match) + printf("describe '%s'@%d: rv==%d\n", + test_str, + cnode->node, + ret); + if (descriptions != NULL) + { + for (j = 0; j < vector_active(descriptions); j++) + { + struct cmd_token *cmd = vector_slot(descriptions, j); + printf(" '%s' '%s'\n", cmd->cmd, cmd->desc); + } + vector_free(descriptions); + } + } + cmd_free_strvec(vline); +} + +int +main(int argc, char **argv) +{ + int opt; + struct prng *prng; + struct vty *vty; + unsigned int edit_distance; + unsigned int max_edit_distance; + unsigned int node_index; + int verbose; + unsigned int test_cmd; + unsigned int iteration; + unsigned int num_iterations; + + max_edit_distance = 3; + node_index = -1; + verbose = 0; + + while ((opt = getopt(argc, argv, "e:n:v")) != -1) + { + switch (opt) + { + case 'e': + max_edit_distance = atoi(optarg); + break; + case 'n': + node_index = atoi(optarg); + break; + case 'v': + verbose++; + break; + default: + fprintf(stderr, "Usage: %s [-e ] [-n ] [-v]\n", argv[0]); + exit(1); + break; + } + } + + test_init(); + prng = prng_new(0); + + vty = vty_new(); + vty->type = VTY_TERM; + + fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds)); + for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) + { + for (edit_distance = 0; + edit_distance <= max_edit_distance; + edit_distance++) + { + num_iterations = 1 << edit_distance; + num_iterations *= num_iterations * num_iterations; + + for (iteration = 0; iteration < num_iterations; iteration++) + test_run(prng, vty, vector_slot(test_cmds, test_cmd), edit_distance, node_index, verbose); + } + fprintf(stderr, "\r%u/%u", test_cmd + 1, vector_active(test_cmds)); + } + fprintf(stderr, "\nDone.\n"); + + vty_close(vty); + prng_free(prng); + test_terminate(); + return 0; +} diff --git a/tests/test-memory.c b/tests/test-memory.c index 2971160b..807249ea 100644 --- a/tests/test-memory.c +++ b/tests/test-memory.c @@ -1,3 +1,22 @@ +/* + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include diff --git a/tests/test-nexthop-iter.c b/tests/test-nexthop-iter.c new file mode 100644 index 00000000..25037932 --- /dev/null +++ b/tests/test-nexthop-iter.c @@ -0,0 +1,291 @@ +/* + * Recursive Nexthop Iterator test. + * This tests the ALL_NEXTHOPS_RO macro. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include "zebra/rib.h" +#include "prng.h" + +struct thread_master *master; +static int verbose; + +static void +str_append(char **buf, const char *repr) +{ + if (*buf) + { + *buf = realloc(*buf, strlen(*buf) + strlen(repr) + 1); + assert(*buf); + strncpy((*buf) + strlen(*buf), repr, strlen(repr) + 1); + } + else + { + *buf = strdup(repr); + assert(*buf); + } +} + +static void +str_appendf(char **buf, const char *format, ...) +{ + va_list ap; + int rv; + char *pbuf; + + va_start(ap, format); + rv = vasprintf(&pbuf, format, ap); + va_end(ap); + assert(rv >= 0); + + str_append(buf, pbuf); + free(pbuf); +} + +/* This structure contains a nexthop chain + * and its expected representation */ +struct nexthop_chain +{ + /* Head of the chain */ + struct nexthop *head; + /* Last nexthop in top chain */ + struct nexthop *current_top; + /* Last nexthop in current recursive chain */ + struct nexthop *current_recursive; + /* Expected string representation. */ + char *repr; +}; + +static struct nexthop_chain* +nexthop_chain_new(void) +{ + struct nexthop_chain *rv; + + rv = calloc(sizeof(*rv), 1); + assert(rv); + return rv; +} + +static void +nexthop_chain_add_top(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + if (nc->head) + { + nc->current_top->next = nh; + nh->prev = nc->current_top; + nc->current_top = nh; + } + else + { + nc->head = nc->current_top = nh; + } + nc->current_recursive = NULL; + str_appendf(&nc->repr, "%p\n", nh); +} + +static void +nexthop_chain_add_recursive(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + assert(nc->current_top); + if (nc->current_recursive) + { + nc->current_recursive->next = nh; + nh->prev = nc->current_recursive; + nc->current_recursive = nh; + } + else + { + SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE); + nc->current_top->resolved = nh; + nc->current_recursive = nh; + } + str_appendf(&nc->repr, " %p\n", nh); +} + +static void +nexthop_chain_clear(struct nexthop_chain *nc) +{ + struct nexthop *tcur, *tnext; + + for (tcur = nc->head; tcur; tcur = tnext) + { + tnext = tcur->next; + if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE)) + { + struct nexthop *rcur, *rnext; + for (rcur = tcur->resolved; rcur; rcur = rnext) + { + rnext = rcur->next; + free(rcur); + } + } + free(tcur); + } + nc->head = nc->current_top = nc->current_recursive = NULL; + free(nc->repr); + nc->repr = NULL; +} + +static void +nexthop_chain_free(struct nexthop_chain *nc) +{ + if (!nc) + return; + nexthop_chain_clear(nc); + free(nc); +} + +/* This function builds a string representation of + * the nexthop chain using the ALL_NEXTHOPS_RO macro. + * It verifies that the ALL_NEXTHOPS_RO macro iterated + * correctly over the nexthop chain by comparing the + * generated representation with the expected representation. + */ +static void +nexthop_chain_verify_iter(struct nexthop_chain *nc) +{ + struct nexthop *nh, *tnh; + int recursing; + char *repr = NULL; + + for (ALL_NEXTHOPS_RO(nc->head, nh, tnh, recursing)) + { + if (recursing) + str_appendf(&repr, " %p\n", nh); + else + str_appendf(&repr, "%p\n", nh); + } + + if (repr && verbose) + printf("===\n%s", repr); + assert((!repr && !nc->repr) || (repr && nc->repr && !strcmp(repr, nc->repr))); + free(repr); +} + +/* This test run builds a simple nexthop chain + * with some recursive nexthops and verifies that + * the iterator works correctly in each stage along + * the way. + */ +static void +test_run_first(void) +{ + struct nexthop_chain *nc; + + nc = nexthop_chain_new(); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_free(nc); +} + +/* This test run builds numerous random + * nexthop chain configurations and verifies + * that the iterator correctly progresses + * through each. */ +static void +test_run_prng(void) +{ + struct nexthop_chain *nc; + struct prng *prng; + int i; + + nc = nexthop_chain_new(); + prng = prng_new(0); + + for (i = 0; i < 1000000; i++) + { + switch (prng_rand(prng) % 10) + { + case 0: + nexthop_chain_clear(nc); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + nexthop_chain_add_top(nc); + break; + case 6: + case 7: + case 8: + case 9: + if (nc->current_top) + nexthop_chain_add_recursive(nc); + break; + } + nexthop_chain_verify_iter(nc); + } + nexthop_chain_free(nc); + prng_free(prng); +} + +int main(int argc, char **argv) +{ + if (argc >= 2 && !strcmp("-v", argv[1])) + verbose = 1; + test_run_first(); + printf("Simple test passed.\n"); + test_run_prng(); + printf("PRNG test passed.\n"); +} diff --git a/tests/test-privs.c b/tests/test-privs.c index a888ea0f..beae81f6 100644 --- a/tests/test-privs.c +++ b/tests/test-privs.c @@ -74,7 +74,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + struct thread_master *master; /* main routine. */ int diff --git a/tests/test-segv.c b/tests/test-segv.c new file mode 100644 index 00000000..55bd25a5 --- /dev/null +++ b/tests/test-segv.c @@ -0,0 +1,61 @@ +/* + * SEGV / backtrace handling test. + * + * copied from test-sig.c + * + * Copyright (C) 2013 by David Lamparter, Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include "lib/log.h" +#include "lib/memory.h" + +struct quagga_signal_t sigs[] = +{ +}; + +struct thread_master *master; + +int +threadfunc (struct thread *thread) +{ + int *null = NULL; + *null += 1; + return 0; +} + +int +main (void) +{ + master = thread_master_create (); + signal_init (master, array_size(sigs), sigs); + + zlog_default = openzlog("testsegv", ZLOG_NONE, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG); + zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + + thread_execute (master, threadfunc, 0, 0); + + exit (0); +} diff --git a/tests/test-sig.c b/tests/test-sig.c index df023fac..7415d7ae 100644 --- a/tests/test-sig.c +++ b/tests/test-sig.c @@ -1,3 +1,22 @@ +/* + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include "lib/log.h" diff --git a/tests/test-stream.c b/tests/test-stream.c index 785ce588..5997b47d 100644 --- a/tests/test-stream.c +++ b/tests/test-stream.c @@ -1,3 +1,25 @@ +/* Simple stream test. + * + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include diff --git a/tests/test-timer-correctness.c b/tests/test-timer-correctness.c new file mode 100644 index 00000000..94c8f1df --- /dev/null +++ b/tests/test-timer-correctness.c @@ -0,0 +1,194 @@ +/* + * Test program to verify that scheduled timers are executed in the + * correct order. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include + +#include + +#include "memory.h" +#include "pqueue.h" +#include "prng.h" +#include "thread.h" + +#define SCHEDULE_TIMERS 800 +#define REMOVE_TIMERS 200 + +#define TIMESTR_LEN strlen("4294967296.999999") + +struct thread_master *master; + +static size_t log_buf_len; +static size_t log_buf_pos; +static char *log_buf; + +static size_t expected_buf_len; +static size_t expected_buf_pos; +static char *expected_buf; + +static struct prng *prng; + +static struct thread **timers; + +static int timers_pending; + +static void terminate_test(void) +{ + int exit_code; + + if (strcmp(log_buf, expected_buf)) + { + fprintf(stderr, "Expected output and received output differ.\n"); + fprintf(stderr, "---Expected output: ---\n%s", expected_buf); + fprintf(stderr, "---Actual output: ---\n%s", log_buf); + exit_code = 1; + } + else + { + printf("Expected output and actual output match.\n"); + exit_code = 0; + } + + thread_master_free(master); + XFREE(MTYPE_TMP, log_buf); + XFREE(MTYPE_TMP, expected_buf); + prng_free(prng); + XFREE(MTYPE_TMP, timers); + + exit(exit_code); +} + +static int timer_func(struct thread *thread) +{ + int rv; + + rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos, + "%s\n", (char*)thread->arg); + assert(rv >= 0); + log_buf_pos += rv; + assert(log_buf_pos < log_buf_len); + XFREE(MTYPE_TMP, thread->arg); + + timers_pending--; + if (!timers_pending) + terminate_test(); + + return 0; +} + +static int cmp_timeval(const void* a, const void *b) +{ + const struct timeval *ta = *(struct timeval * const *)a; + const struct timeval *tb = *(struct timeval * const *)b; + + if (timercmp(ta, tb, <)) + return -1; + if (timercmp(ta, tb, >)) + return 1; + return 0; +} + +int main(int argc, char **argv) +{ + int i, j; + struct thread t; + struct timeval **alarms; + + master = thread_master_create(); + + log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + log_buf_pos = 0; + log_buf = XMALLOC(MTYPE_TMP, log_buf_len); + + expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + expected_buf_pos = 0; + expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len); + + prng = prng_new(0); + + timers = XMALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers)); + + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + long interval_msec; + int ret; + char *arg; + + /* Schedule timers to expire in 0..5 seconds */ + interval_msec = prng_rand(prng) % 5000; + arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1); + timers[i] = thread_add_timer_msec(master, timer_func, arg, interval_msec); + ret = snprintf(arg, TIMESTR_LEN + 1, "%ld.%06ld", + timers[i]->u.sands.tv_sec, timers[i]->u.sands.tv_usec); + assert(ret > 0); + assert((size_t)ret < TIMESTR_LEN + 1); + timers_pending++; + } + + for (i = 0; i < REMOVE_TIMERS; i++) + { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + if (!timers[index]) + continue; + + XFREE(MTYPE_TMP, timers[index]->arg); + thread_cancel(timers[index]); + timers[index] = NULL; + timers_pending--; + } + + /* We create an array of pointers to the alarm times and sort + * that array. That sorted array is used to generate a string + * representing the expected "output" of the timers when they + * are run. */ + j = 0; + alarms = XMALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms)); + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + if (!timers[i]) + continue; + alarms[j++] = &timers[i]->u.sands; + } + qsort(alarms, j, sizeof(*alarms), cmp_timeval); + for (i = 0; i < j; i++) + { + int ret; + + ret = snprintf(expected_buf + expected_buf_pos, + expected_buf_len - expected_buf_pos, + "%ld.%06ld\n", alarms[i]->tv_sec, alarms[i]->tv_usec); + assert(ret > 0); + expected_buf_pos += ret; + assert(expected_buf_pos < expected_buf_len); + } + XFREE(MTYPE_TMP, alarms); + + while (thread_fetch(master, &t)) + thread_call(&t); + + return 0; +} diff --git a/tests/test-timer-performance.c b/tests/test-timer-performance.c new file mode 100644 index 00000000..a529a5ce --- /dev/null +++ b/tests/test-timer-performance.c @@ -0,0 +1,104 @@ +/* + * Test program which measures the time it takes to schedule and + * remove timers. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include +#include + +#include + +#include "thread.h" +#include "pqueue.h" +#include "prng.h" + +#define SCHEDULE_TIMERS 1000000 +#define REMOVE_TIMERS 500000 + +struct thread_master *master; + +static int dummy_func(struct thread *thread) +{ + return 0; +} + +int main(int argc, char **argv) +{ + struct prng *prng; + int i; + struct thread **timers; + struct timeval tv_start, tv_lap, tv_stop; + unsigned long t_schedule, t_remove; + + master = thread_master_create(); + prng = prng_new(0); + timers = calloc(SCHEDULE_TIMERS, sizeof(*timers)); + + /* create thread structures so they won't be allocated during the + * time measurement */ + for (i = 0; i < SCHEDULE_TIMERS; i++) + timers[i] = thread_add_timer_msec(master, dummy_func, NULL, 0); + for (i = 0; i < SCHEDULE_TIMERS; i++) + thread_cancel(timers[i]); + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_start); + + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + long interval_msec; + + interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS); + timers[i] = thread_add_timer_msec(master, dummy_func, + NULL, interval_msec); + } + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_lap); + + for (i = 0; i < REMOVE_TIMERS; i++) + { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + if (timers[index]) + thread_cancel(timers[index]); + timers[index] = NULL; + } + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_stop); + + t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec); + t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000; + + t_remove = 1000 * (tv_stop.tv_sec - tv_lap.tv_sec); + t_remove += (tv_stop.tv_usec - tv_lap.tv_usec) / 1000; + + printf("Scheduling %d random timers took %ld.%03ld seconds.\n", + SCHEDULE_TIMERS, t_schedule/1000, t_schedule%1000); + printf("Removing %d random timers took %ld.%03ld seconds.\n", + REMOVE_TIMERS, t_remove/1000, t_remove%1000); + fflush(stdout); + + free(timers); + thread_master_free(master); + prng_free(prng); + return 0; +} diff --git a/tests/testcommands.in b/tests/testcommands.in new file mode 100644 index 00000000..7fe62799 --- /dev/null +++ b/tests/testcommands.in @@ -0,0 +1,216 @@ +# +# +# Some randomly chosen valid commands +# +# +area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE +area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1 +area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1 +clear bgp 1 out +clear bgp ipv6 2001:db8::1 out +clear bgp view VARIABLE * soft +clear ip bgp 1.2.3.4 ipv4 multicast out +ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig +ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1 +network 1.0.0.0/8 area 0 +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval +no bgp graceful-restart +no ipv6 nd mtu 1 +no neighbor 1.2.3.4 distribute-list 1 in +no neighbor 2001:db8::1 send-community both +no neighbor VARIABLE maximum-prefix +redistribute isis route-map VARIABLE metric 0 metric-type 2 +redistribute rip metric 0 route-map VARIABLE metric-type 1 +show bgp community VARIABLE local-AS no-export VARIABLE exact-match +show bgp ipv6 community no-advertise no-export no-export no-export +show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match +show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match +show bgp view VARIABLE +show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE +show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS +show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise +show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE +show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS +show ip bgp community no-advertise local-AS no-advertise VARIABLE +show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match +show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match +show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match +show ipv6 bgp community no-export no-export VARIABLE VARIABLE +show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match +show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match +show ipv6 mbgp community local-AS local-AS no-export no-export exact-match +show ipv6 mbgp community no-export no-export local-AS no-export exact-match +show ipv6 ospf6 database as-external dump +show ipv6 ospf6 database inter-prefix 1.2.3.4 detail +show ipv6 ospf6 database intra-prefix 1.2.3.4 internal +# +# +# Slightly Fuzzed commands +# +# +a8ra 0 range 1.0.0.0/8 adverOise +accept-lifetime VARIABE 1 VA6IABLE 19I3 VARIABLE 1 VARIABLE 1993 +arAea 1.2.M.4 virtual-link 1.2.3.4 dead-interval 1 dead-interval 1 dead-inter6val 1 transmit-delay 1 +area 0 virtu0al-link 1.2.3.i hello-interval 1 ello-interval 1 transmit-delay 1 retransmit-interval 1 +area 0 virtual-lin 1.2.3.4 retransmit-interval 1 tranwmit-delay 1 retransmit-interval 1 retransmit-interval 1 +area 0 virtual-link 1.2.3.4 retransmit-interal 1 trasmit-dely 1 +area 1.2.3.4 virtual-link 1.2.3.4 deadCinterval 1 dead-intervalK 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 dead-intervalo I1 dead-interval 1 retransmit-interval1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1 +area 1.2.3.4 virtuyl-link 1.2.3.4 dead-interval 1 dead-inervalI 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virual-link 1.2.34 retransmit-interval 1 dead-interval 1 dead-interva 1 +area1.2.83.4 virtual-link 1.2.3.4 retra0smit-interval 1 dead-interval 1 dead-interval 1 +clear bgAp 2001g:dbK::1 +clear ip bgp 1.2.3.4 pv4 mlticat out +cleau bg i2001:db8::1 rsclient +de:ug ospf6 messag2 lsreq :recv +how ip bgp communiQy no-advertise no-adve:tise no-advertise +ip route 1.0Q0.0/8 1.2.3.s4 reGject +ipv6 nd prefix 2O01:db8::/32 0 infinEite off-link +ipv6 nwd prefix 2001:db8::/32 0 infinite oUUff-link +ipv6 route 2001:db8::/32q2001:db8:k: blackhole 1 +kshow ip rIute bgp +matcch peer .2.30.4 +mcogin +mhow ipv6 mbgp community o-advertise yocal-AS no-advertise +neighbor1.2..4 attribute-unchnged next-hop +neihbcr 2001:d b8::1 distribute-list 1 in +nko key-tqring +no area 0 viertual-link 1.2.3k.4 retransmit-iterval retransmit-interval retransmit-interval hello-interval +no area 0 virtual-link 1.2.3.4 dead-intaerval dead-intervIl hello-interval retransmit-interval +no area 0 virtual-link 1.2.3.4 retransmit-interval retransmit-intervIl dead-interval tranImit-deqlay +no area 0 virtual-link S1.2.3.4 d-ead-interval hello-interval transmit-deay transmit-delay +no area 1.2.3.4 virtua -link 1.2.3.4 transmit-delay hello-interval hello-interval retransmt-interval +no area 1.2.3.4 virtual-link 1.2.3.4 dea-iterval retransmit-interva- dead-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interSval dead-interval retransmit-interval transmitdelay +no a:rea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interSvalW dead-interval retransmit-interval hello-interval +noarea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval trynsmit-delay hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmt:delay retransmit-interval retransmit-interval dead-Mnterval +no ares 1.2.3.4 virtual-link 1.2.3.4 dead-interval retransmit-interval dead-inesval retransmit-interval +no ayrea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval transmi-delay hello-interval +no bg2 grace2fuy-restart +no debug ospk6 nter2face +noimatch ipv6 addrMss VARIABLE +nomStch iA next-hop prefix-list +no neighbCr 200 :db8::1oroute-map VARIABLE export +no neighbor VARIABLE attributeaw8changed next-hop +no orea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval ead-interval retransmit-inteSval hello-interval +no ospcdead-inkerval +no redistribute kernelrote-map VARIABLE metric 0 +no redistribute s4taik metric 0 +nos Ceighbor 1.2.3.4 route-mapEVARIABLE in +o :neighbor VAIABLE attribute-unchanged next-hop +ooa router ip +redistribute isis meGtric-type2 Q route-map VARIABLE +redistribute static metric-type 1 metri 0 rowute-map VARIABLE +set-Koveroadbit +sh2w ipv6 mbgp comAunity VARIABLE +shgw bgp ipv6 community no-export VARIABLE no-xport no-expmrt +shiow Wgp neighbors +shoAw ip bgpipv4 unicast com6munity no-export no-export no-advertise no-export exact-match +sho bgp view gARIABLE nyeighbors 2001:db8::1 received-routes +shoow bgp ommunity local-AS no--export +show6 bgp community no-advertise local4-AS no-advertise VARIABLE exact-math +show8 bgp view VARIABLE ipv4 multicast community ARIABLE VARIABLE local-S +show bgp cCommunity VARIABLE VOARIABL no-advertise +show bgp cimAunity loal-AS local-AS no-export local-AS +show bgp cmmunity n-advertise no-export local-S no-advertise +show bgp communi0y no-export no-Cexport no-0xport no-export +show bgp communityOlocal-A no-advertise local-WAS +show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match +show bgp communiy no-export no-adsvertise VARIABLOE local-AS +show bgp communiYty no-export VARIABLE VARIABLE locali-AS exact-math +show bgp commuUityW no-advertis local-AS no-advertise no-advertise +show bgp commuWnity VAIABLE local-AS no-advertise n-export +show bgp com:unity no-exportqno-export VARIABLE no-expoIrt exact-match +show bgp ipv6 community local-AS no-expor no-xport VARIABCLE +show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE +show bgp ipv6 community no-advertise no6-export lcal-AS local-AS +show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math +show bgp ipv6 comm-unity no-advertise no-export local-AS local-kS exact-match +show bgp ipv6 community no-export local-AS no-adertise no-adve-tie +show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE +show bgp naighbors 201:db8::1 rUeceived-routes +show bgp viewVAIABLE ipv4 multicast community VARIABLE4no-export no-advertise local-AS +show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS +show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export +show bgp view VARIABLE ipv4 multicast omsunity local-AS VARIABLE no-advertise nUo-export +show bgp view VARIABLE ipv4 mutiast community no-export no-export VARIBLE no-export +show bgp view VARIABLE ipv4 unicast 0community VARIABqLE local-AS no-export VARIABwE +show bgp view VARIABLE ipv4 unicast communeity no-export AcRIABLE no-advertise local-AS +show bgp view VARIABLE ipv4 unicasU comunity no-export VARIABL no-advertise +show bgp view VARIABLE ipv6 unicast cocmmunity VARIABLE no-advet6ise VARIABLE +show bgp view VARIABLE ipvk4 unicast communty no-advertie local-AS local-AS no-export +show bgp view VARIALE ipv4 multicast cyommunity no-xport local-AS local-AS +show i6 bge community no-export VARIABLE no-advegtise VARIABLE exact-match +show iI bgp community no-advertise no-ad2vertsse VARIABLE exact-match +show ip6osp6 database dump +show ipA6 bgp community local-AS local-AS no-advertse lo:cal-AS +show ip bg comunity VARIABLE lcal-AS no-advertise +show ip bgp communityno-export2no-export no-advertise locaE-AS +Show ip bgp community no-export loqcal-AS no-adverise no-export +show ip bgp community no-expor VARIABLEono-export VARIAuBLE +show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match +show ip bgp cWmmunity no-expoWrt VARIABLE no-advertise VARIABLEexact-match +show ip bgp ip4 nicast community no-advertise no-expoIt local-AS local-AS exact-match +show ip bgp ipAv4 multicast community no-export no-export no-export no-advertiqe exact-mach +show ip bgp ipv4 Aulticast community no-advertise VARIABLE no-advertisKe no-exort +show ip bgp ipv4 meuqlticast community VARIABLE VARIABLE no-export n-export +show ip bgp ipv4 mlticast coQmmunity localg-AS local-AS no-advertise local-AS +show ip bgp ipv4 multicast communiy VARIABLE no-export VARIABLE no-advertise yxact-atch +show ip bgp ipv4 unicast commu0nity local-AS no-export no-exrt VARIABLE exact-match +show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match +showip bgp ipv4 unicast community no-export VARIABLE no-exp-ort VAR6IABLE exact-match +show ip bgp ipv4 unicat community no-exportlocal-AS VARIABLE no-export exa0t-match +show ip bgp ipv4 unicst community no-advertiseG local-AS no-advertise +show ip bgp i:v4 multicast community VARIABLE VARIABLE VARIABLE no-export eMxact-match +show ip bgp Mv4 unicast community no-export VARIABLE VARIABLE VAoRIABLE +show ipgexecommunity-list 1 +show ipkv6 bgp community no-export no-export VARIABL VARIBLE +show ipv6 bgp commu2nity local-AS local-AS noEadvertise local-AS +show ipv6 bgp communitK VARIABLE lcocal-AS no-advertie no-advertise exact-match +show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match +show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE +show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match +show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE +show ipv6 bgp comu-ity VARIABLE local-AS no-advertise no-export exact-match +show ipv6 bgp comunity no- export local-AS no-advertisge VARIABLE +show ipv6 bgp ommunity sno-advcrtise VARIABLE no-export no-advertise exact-match +show ipv6 igp community no-advertise no-advertise no-ecxpo0rt no-export +show ipv6 mb communyty VARIABLE +show ipv6 osp8f6 database nQtwork adv-ruter 1.2.3.4 detail +show ipv6 ospf6 dataase type-7 adv-router 1.2.3.4 inernal +show ipv6 ospf6 Edatabase intuer8-prefix 1.2.3.4 detail +show ipvq6 ospf6 database as-externa detil +show ip Wbgp ipv4 unicast community no-advertise no-exprt no-export VARIABLEK exact-match +show ip Ybgp attribute-in ufo +showMbgp ipv6 community ARIABLE local-AS local-AS no8advertise exact-match +show p bgp community no-dvertise no-export no-advertiseIno-export exact-match +show uipv6 mbgp coqmmunKty VARIABLE +shQw ipv6 mbgp community no-advetise local-AS no-export no-export ex8ct-match +shuw ipv6 mbgp community VARIABLyUE no-export no-export no-advertise +shw bgp view VARIABLE ipv4 un0icast Gcommunity no-export VARIABLE no-advertise +sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise +sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4 +Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS +Wneighbor 1.2.3.4 dot-capabiliy-negotiate +# +# +# Some teststrings explicitly used for keyword commands +# +# +redistribute bgp +redistribute bgp m 10 +redistribute bgp metric 10 metric-type 1 +redistribute bgp metric 10 metric 10 +redistribute bgp route-map RMAP_REDIST_BGP +default-information originate metric-type 1 metric 10 +default-information originate always metric-type 1 metric 10 +default-information originate route-map RMAP_DEFAULT +default-information originate route-map RMAP_DEFAULT metric 10 +default-information originate always metric-type 2 metric 23 diff --git a/tests/testcommands.refout b/tests/testcommands.refout new file mode 100644 index 00000000..11483b84 --- /dev/null +++ b/tests/testcommands.refout @@ -0,0 +1,1007 @@ +execute relaxed 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +execute strict 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +complete 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0 + 'KEY' 'The OSPF password (key)' +execute relaxed 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +complete 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +execute strict 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +complete 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +execute strict 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +complete 'clear bgp 1 out'@4: rv==7 + 'out' +describe 'clear bgp 1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +execute strict 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +complete 'clear bgp ipv6 2001:db8::1 out'@4: rv==7 + 'out' +describe 'clear bgp ipv6 2001:db8::1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +execute strict 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +complete 'clear bgp view VARIABLE * soft'@4: rv==7 + 'soft' +describe 'clear bgp view VARIABLE * soft'@4: rv==0 + 'soft' 'Soft reconfig' +execute relaxed 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +execute strict 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +complete 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==7 + 'out' +describe 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +execute strict 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +complete 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==7 + 'no-autoconfig' +describe 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0 + 'no-autoconfig' 'Do not use prefix for autoconfiguration' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0 + '<1-255>' 'Distance value for this prefix' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +execute relaxed 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +execute strict 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +complete 'network 1.0.0.0/8 area 0'@23: rv==2 +describe 'network 1.0.0.0/8 area 0'@23: rv==0 + '<0-4294967295>' 'OSPF area ID as a decimal value' + 'A.B.C.D' 'OSPF area ID in IP address format' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +complete 'no bgp graceful-restart'@17: rv==7 + 'graceful-restart' +describe 'no bgp graceful-restart'@17: rv==0 + 'graceful-restart' 'Graceful restart capability parameters' +execute relaxed 'no bgp graceful-restart'@18: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@18: rv==2 +complete 'no bgp graceful-restart'@18: rv==2 +describe 'no bgp graceful-restart'@18: rv==2 +execute relaxed 'no bgp graceful-restart'@19: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@19: rv==2 +complete 'no bgp graceful-restart'@19: rv==2 +describe 'no bgp graceful-restart'@19: rv==2 +execute relaxed 'no bgp graceful-restart'@20: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@20: rv==2 +complete 'no bgp graceful-restart'@20: rv==2 +describe 'no bgp graceful-restart'@20: rv==2 +execute relaxed 'no bgp graceful-restart'@21: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@21: rv==2 +complete 'no bgp graceful-restart'@21: rv==2 +describe 'no bgp graceful-restart'@21: rv==2 +execute relaxed 'no bgp graceful-restart'@22: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@22: rv==2 +complete 'no bgp graceful-restart'@22: rv==2 +describe 'no bgp graceful-restart'@22: rv==2 +execute relaxed 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +execute strict 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +complete 'no ipv6 nd mtu 1'@11: rv==2 +describe 'no ipv6 nd mtu 1'@11: rv==0 + '<1-65535>' 'MTU in bytes' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@17: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@17: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@18: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@18: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@19: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@19: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@20: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@20: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@21: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@21: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@22: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@22: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@17: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@17: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@18: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@18: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@19: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@19: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@20: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@20: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@21: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@21: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +execute strict 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +complete 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==7 + '2' +describe 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0 + '2' 'Set OSPF External Type 2 metrics' +execute relaxed 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +execute strict 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +complete 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==7 + '1' +describe 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp view VARIABLE'@1: rv==4 +execute strict 'show bgp view VARIABLE'@1: rv==4 +complete 'show bgp view VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE'@1: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE'@2: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE'@4: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@2: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@2: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@4: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@4: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'dead-interva', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@14: rv==7 + 'bgp' +describe 'redistribute bgp'@14: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@15: rv==7 + 'bgp' +describe 'redistribute bgp'@15: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +complete 'redistribute bgp'@16: rv==7 + 'bgp' +describe 'redistribute bgp'@16: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +execute strict 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +complete 'redistribute bgp'@23: rv==7 + 'bgp' +describe 'redistribute bgp'@23: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@24: rv==7 + 'bgp' +describe 'redistribute bgp'@24: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp m 10'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@14: rv==2 +complete 'redistribute bgp m 10'@14: rv==2 +describe 'redistribute bgp m 10'@14: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@15: rv==2 +complete 'redistribute bgp m 10'@15: rv==2 +describe 'redistribute bgp m 10'@15: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@23: rv==3 +execute strict 'redistribute bgp m 10'@23: rv==2 +complete 'redistribute bgp m 10'@23: rv==3 +describe 'redistribute bgp m 10'@23: rv==3 +execute relaxed 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +execute strict 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +complete 'redistribute bgp metric 10 metric-type 1'@23: rv==7 + '1' +describe 'redistribute bgp metric 10 metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0 + 'WORD' 'Route map name' +execute relaxed 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +execute strict 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +complete 'default-information originate metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +execute strict 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +complete 'default-information originate always metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate always metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +execute strict 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +complete 'default-information originate always metric-type 2 metric 23'@23: rv==2 +describe 'default-information originate always metric-type 2 metric 23'@23: rv==0 + '<0-16777214>' 'OSPF metric' diff --git a/tools/mrlg.cgi b/tools/mrlg.cgi deleted file mode 100755 index ac468eef..00000000 --- a/tools/mrlg.cgi +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/perl -## -## Zebra Looking Glass version 1.0 -## 01 FEB 2000 -## Copyright (C) 2000 John W. Fraizer III -## *All* copyright notices must remain in place to use this code. -## -## The latest version of this code is available at: -## ftp://ftp.enterzone.net/looking-glass/ -## -## -## This file is part of GNU Zebra. -## -## GNU Zebra 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. -## -## GNU Zebra 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 General Public License -## along with GNU Zebra; see the file COPYING. If not, write to the -## Free Software Foundation, Inc., 59 Temple Place - Suite 330, -## Boston, MA 02111-1307, USA. - -require 5.002; -use POSIX; -use Net::Telnet (); - - - -## Set the URL for your site. -$url="http://www.sample.com/mrlg.cgi"; - -## Set your router variables in sub set_router and modify the selections in Main to match. - - -############################################################ -#Main -############################################################ -{ - -## Set the router default -@Form{'router'} = "router1"; - -## Get the form results now so we can override the default router -get_form(); - -print "Content-type: text/html\n\n"; - -print ' - - -Multi-Router Looking Glass for Zebra - - - - -

Multi-Router Looking Glass for Zebra

-Copyright 2000 - John Fraizer, EnterZone Inc. -
-'; - -print ' - -'; -print "
\n"; -print "Router: -

-Query: -
-show ip bgp
-show ip bgp summary
-show ip route
-show interface
-show ipv6 bgp
-show ipv6 bgp summary
-show ipv6 route
-
-Argument: -
-'; - -## Set up the address, pw and ports, etc for the selected router. -set_router(); - -## Set up which command is to be executed (and then execute it!) -set_command(); - - -print ' -

-
- -Multi-Router Looking Glass for Zebra version 1.0
-Written by: John Fraizer - -EnterZone, Inc
-Source code: ftp://ftp.enterzone.net/looking-glass/ - - -'; - -## All done! - -exit (0); -} - - -############################################################ -sub get_form -############################################################ -{ - - #read STDIN - read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); - - # Split the name-value pairs - @pairs = split(/&/, $buffer); - - # For each name-value pair: - foreach $pair (@pairs) - { - - # Split the pair up into individual variables. - local($name, $value) = split(/=/, $pair); - - # Decode the form encoding on the name and value variables. - $name =~ tr/+/ /; - $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; - - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; - - # If they try to include server side includes, erase them, so they - # aren't a security risk if the html gets returned. Another - # security hole plugged up. - $value =~ s///g; - - @Form{$name} = $value ; - - } - -} - -############################################################ -sub set_router -############################################################ - -## $server is the IP address of the router running zebra -## $login_pass is the password of the router -## $bgpd is the port that bgpd will answer on -## $zebra is the port that zebra will answer on -## if $zebra is "", it will disable sh ip route and sh int for that router. -## if $full_tables is set to "1" for a router, full BGP and IP ROUTE table dumps will be allowed via the looking glass. -## This is a BAD thing to do if you have multiple full views on a router. That's why the option is there. - -{ -if ($Form{'router'} eq 'router1') - { -$server = '10.1.1.1'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = ""; -$full_tables=1; - - } - -elsif ($Form{'router'} eq 'router2') - { -$server = '10.1.1.2'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; - } - -elsif ($Form{'router'} eq 'router3') - { -$server = '10.1.1.3'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; -$full_tables=1; - } - -elsif ($Form{'router'} eq 'router4') - { -$server = '10.1.1.4'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; - } - - -} - - -############################################################ -sub set_command -############################################################ -{ -if ($Form{'query'} eq '1') - { - sh_ip_bgp('ip'); - } - -elsif ($Form{'query'} eq '2') - { - sh_ip_bgp_sum('ip'); - } - -if ($Form{'query'} eq '3') - { - sh_ip_route('ip'); - } - -if ($Form{'query'} eq '4') - { - sh_int(); - } -if ($Form{'query'} eq '5') - { - sh_ip_bgp('ipv6'); - } -if ($Form{'query'} eq '6') - { - sh_ip_bgp_sum('ipv6'); - } -if ($Form{'query'} eq '7') - { - sh_ip_route('ipv6'); - } -} -############################################################ -sub sh_ip_bgp -############################################################ -{ -my $protocol = shift(@_); -$port = $bgpd; -if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } -$command = "show $protocol bgp $Form{'arg'}"; -if ($Form{'arg'} eq '') - { - if ($full_tables eq '1') - { - execute_command(); - } - else - { - print "Sorry. Displaying the FULL routing table would put too much load on the router!\n\n"; - } - } -else - { - execute_command(); - } -} - -############################################################ -sub sh_ip_bgp_sum -############################################################ -{ - my $protocol = shift(@_); - $port = $bgpd; - if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } - $command = "show $protocol bgp summary"; - execute_command(); -} - -############################################################ -sub sh_ip_route -############################################################ -{ - -if ($zebra eq '') - { - print "Sorry. The show ip route command is disabled for this router." - } -else - { - - $port = $zebra; - my $protocol = shift(@_); - if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } - $command = "show $protocol route $Form{'arg'}"; - if ($Form{'arg'} eq '') - { - if ($full_tables eq '1') - { - execute_command(); - } - else - { - print "Sorry. Displaying the FULL routing table would put too much load on the router!\n\n"; - } - } - else - { - execute_command(); - } - } -} - -############################################################ -sub sh_int -############################################################ -{ -if ($zebra eq '') - { - print "Sorry. The show interface command is disabled for this router." - } -else - { - $port = $zebra; - $command = "show interface $Form{'arg'}"; - execute_command(); - } -} - - - -############################################################ -sub execute_command -############################################################ -## This code is based on: -## -## Zebra interactive console -## Copyright (C) 2000 Vladimir B. Grebenschikov -## - - -{ - -print "Executing command = $command"; - -# my $port = ($opt_z ? 'zebra' : 0) || -# ($opt_b ? 'bgpd' : 0) || -# ($opt_o ? 'ospfd' : 0) || -# ($opt_r ? 'ripd' : 0) || 'bgpd'; - -my $cmd = $command; - - - my $t = new Net::Telnet (Timeout => 10, - Prompt => '/[\>\#] $/', - Port => $port); - - $t->open ($server); - - $t->cmd ($login_pass); - - if ($cmd) - { - docmd ($t, $cmd); - } - -} - -############################################################ -sub docmd -############################################################ -{ - my ($t, $cmd) = @_; - my @lines = $t->cmd ($cmd); - print "
\n";
-  print join ('', grep (!/[\>\#] $/, @lines)), "\n";
-  print "
"; -} - - - diff --git a/tools/mrlg.txt b/tools/mrlg.txt new file mode 100644 index 00000000..0ebf7ee6 --- /dev/null +++ b/tools/mrlg.txt @@ -0,0 +1,5 @@ +The Multi-Router Looking Glass (MRLG) CGI script now lives at: + + http://mrlg.op-sec.us/ + +Please obtain the latest version from there. diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 7550173c..a66615be 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" LIBS = @LIBS@ @CURSES@ @LIBPAM@ @@ -25,6 +25,7 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \ $(top_srcdir)/ospfd/*.c $(top_srcdir)/ospf6d/*.c \ $(top_srcdir)/ripd/*.c $(top_srcdir)/ripngd/*.c \ $(top_srcdir)/babeld/*.c \ + $(top_srcdir)/pimd/pim_cmd.c \ $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ @@ -33,7 +34,8 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \ - $(top_srcdir)/zebra/zebra_routemap.c + $(top_srcdir)/zebra/zebra_routemap.c \ + $(top_srcdir)/zebra/zebra_fpm.c vtysh_cmd.c: $(vtysh_cmd_FILES) ./$(EXTRA_DIST) $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 37763043..d323cdb0 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -90,41 +90,40 @@ foreach (@ARGV) { $cmd =~ s/\s+$//g; # $protocol is VTYSH_PROTO format for redirection of user input - if ($file =~ /lib/) { - if ($file =~ /keychain.c/) { - $protocol = "VTYSH_RIPD"; - } - if ($file =~ /routemap.c/) { - $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; - } - if ($file =~ /filter.c/) { - $protocol = "VTYSH_ALL"; - } - if ($file =~ /plist.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; - } else { - $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA"; - } - } - if ($file =~ /distribute.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD"; - } else { - $protocol = "VTYSH_RIPD"; - } - } - if ($file =~ /if_rmap.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD"; - } else { - $protocol = "VTYSH_RIPD"; - } - } - if ($file =~ /vty.c/) { - $protocol = "VTYSH_ALL"; - } - } else { + if ($file =~ /lib\/keychain\.c$/) { + $protocol = "VTYSH_RIPD"; + } + elsif ($file =~ /lib\/routemap\.c$/) { + $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; + } + elsif ($file =~ /lib\/filter\.c$/) { + $protocol = "VTYSH_ALL"; + } + elsif ($file =~ /lib\/plist\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; + } else { + $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA"; + } + } + elsif ($file =~ /lib\/distribute\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD"; + } else { + $protocol = "VTYSH_RIPD"; + } + } + elsif ($file =~ /lib\/if_rmap\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD"; + } else { + $protocol = "VTYSH_RIPD"; + } + } + elsif ($file =~ /lib\/vty\.c$/) { + $protocol = "VTYSH_ALL"; + } + else { ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); $protocol = "VTYSH_" . uc $protocol; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index e3709e07..cc09eec9 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -59,6 +59,7 @@ struct vtysh_client { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH}, { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH}, { .fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .path = BABEL_VTYSH_PATH}, + { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH}, }; @@ -268,7 +269,7 @@ vtysh_pager_init (void) if (pager_defined) vtysh_pager_name = strdup (pager_defined); else - vtysh_pager_name = strdup ("more"); + vtysh_pager_name = strdup ("cat"); } /* Command execution over the vty interface. */ @@ -554,7 +555,7 @@ vtysh_rl_describe (void) vector vline; vector describe; int width; - struct desc *desc; + struct cmd_token *token; vline = cmd_make_strvec (rl_line_buffer); @@ -592,15 +593,15 @@ vtysh_rl_describe (void) /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { int len; - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') + len = strlen (token->cmd); + if (token->cmd[0] == '.') len--; if (width < len) @@ -608,19 +609,19 @@ vtysh_rl_describe (void) } for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - if (! desc->str) + if (! token->desc) fprintf (stdout," %-s\n", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd); else fprintf (stdout," %-*s %s\n", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc); } cmd_free_strvec (vline); @@ -677,8 +678,9 @@ new_completion (char *text, int start, int end) if (matches) { rl_point = rl_end; - if (complete_status == CMD_COMPLETE_FULL_MATCH) - rl_pending_input = ' '; + if (complete_status != CMD_COMPLETE_FULL_MATCH) + /* only append a space on full match */ + rl_completion_append_character = '\0'; } return matches; @@ -1884,7 +1886,7 @@ DEFUN (vtysh_terminal_length, { int lines; char *endptr = NULL; - char default_pager[10]; + char default_pager[12]; lines = strtol (argv[0], &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') @@ -1901,7 +1903,7 @@ DEFUN (vtysh_terminal_length, if (lines != 0) { - snprintf(default_pager, 10, "more -%i", lines); + snprintf(default_pager, 12, "head -n %i", lines); vtysh_pager_name = strdup (default_pager); } @@ -2211,12 +2213,9 @@ void vtysh_readline_init (void) { /* readline related settings. */ - rl_bind_key ('?', (Function *) vtysh_rl_describe); + rl_bind_key ('?', (rl_command_func_t *) vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; - rl_attempted_completion_function = (CPPFunction *)new_completion; - /* do not append space after completion. It will be appended - * in new_completion() function explicitly. */ - rl_completion_append_character = '\0'; + rl_attempted_completion_function = (rl_completion_func_t *)new_completion; } char * diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 3cc7bafe..1681a71a 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -30,9 +30,10 @@ #define VTYSH_BGPD 0x20 #define VTYSH_ISISD 0x40 #define VTYSH_BABELD 0x80 -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD +#define VTYSH_PIMD 0x100 +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_BABELD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD /* vtysh local configuration file. */ #define VTYSH_DEFAULT_CONFIG "vtysh.conf" diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 4a315a5c..48958f0f 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -299,8 +299,6 @@ main (int argc, char **argv, char **env) vty_init_vtysh (); - sort_node (); - /* Read vtysh configuration file before connecting to daemons. */ vtysh_read_config (config_default); diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am index badaa5b5..9256006c 100644 --- a/watchquagga/Makefile.am +++ b/watchquagga/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\" AM_CFLAGS = $(PICFLAGS) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index ea962bf4..48087917 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -1,23 +1,24 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DMULTIPATH_NUM=@MULTIPATH_NUM@ INSTALL_SDATA=@INSTALL@ -m 600 -LIB_IPV6 = @LIB_IPV6@ LIBCAP = @LIBCAP@ ipforward = @IPFORWARD@ if_method = @IF_METHOD@ -if_proc = @IF_PROC@ rt_method = @RT_METHOD@ rtread_method = @RTREAD_METHOD@ kernel_method = @KERNEL_METHOD@ -other_method = @OTHER_METHOD@ ioctl_method = @IOCTL_METHOD@ -otherobj = $(ioctl_method) $(ipforward) $(if_method) $(if_proc) \ - $(rt_method) $(rtread_method) $(kernel_method) $(other_method) +otherobj = $(ioctl_method) $(ipforward) $(if_method) \ + $(rt_method) $(rtread_method) $(kernel_method) + +if HAVE_NETLINK +othersrc = zebra_fpm_netlink.c +endif AM_CFLAGS = $(PICFLAGS) AM_LDFLAGS = $(PILDFLAGS) @@ -29,7 +30,8 @@ noinst_PROGRAMS = testzebra zebra_SOURCES = \ zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ - irdp_main.c irdp_interface.c irdp_packet.c router-id.c + irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ + $(othersrc) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c \ @@ -37,24 +39,25 @@ testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ noinst_HEADERS = \ connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \ - interface.h ipforward.h irdp.h router-id.h kernel_socket.h + interface.h ipforward.h irdp.h router-id.h kernel_socket.h \ + rt_netlink.h zebra_fpm.h zebra_fpm_private.h -zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) +zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) -testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) +testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) zebra_DEPENDENCIES = $(otherobj) -EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c if_proc.c \ - if_sysctl.c ipforward_aix.c ipforward_ews.c ipforward_proc.c \ - ipforward_solaris.c ipforward_sysctl.c rt_ioctl.c rt_netlink.c \ - rt_socket.c rtread_netlink.c rtread_proc.c rtread_sysctl.c \ - rtread_getmsg.c kernel_socket.c kernel_netlink.c mtu_kvm.c \ +EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ + if_sysctl.c ipforward_proc.c \ + ipforward_solaris.c ipforward_sysctl.c rt_netlink.c \ + rt_socket.c rtread_netlink.c rtread_sysctl.c \ + rtread_getmsg.c kernel_socket.c kernel_netlink.c \ ioctl.c ioctl_solaris.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB -#client : client_main.o ../lib/libzebra.la -# $(CC) -g -o client client_main.o ../liblzebra.la $(LIBS) $(LIB_IPV6) +client : client_main.o ../lib/libzebra.la + $(CC) -g -o client client_main.o ../liblzebra.la $(LIBS) $(LIB_IPV6) quaggaconfdir = $(sysconfdir) diff --git a/zebra/connected.c b/zebra/connected.c index 05538c5d..2db981b3 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -36,8 +36,8 @@ #include "zebra/interface.h" #include "zebra/connected.h" extern struct zebra_t zebrad; - -/* withdraw a connected address */ + +/* communicate the withdrawal of a connected address */ static void connected_withdraw (struct connected *ifc) { @@ -49,8 +49,9 @@ connected_withdraw (struct connected *ifc) { zebra_interface_address_delete_update (ifc->ifp, ifc); - if_subnet_delete (ifc->ifp, ifc); - + if (ifc->address->family == AF_INET) + if_subnet_delete (ifc->ifp, ifc); + if (ifc->address->family == AF_INET) connected_down_ipv4 (ifc->ifp, ifc); #ifdef HAVE_IPV6 @@ -61,6 +62,9 @@ connected_withdraw (struct connected *ifc) UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); } + /* The address is not in the kernel anymore, so clear the flag */ + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) { listnode_delete (ifc->ifp->connected, ifc); @@ -77,27 +81,22 @@ connected_announce (struct interface *ifp, struct connected *ifc) listnode_add (ifp->connected, ifc); /* Update interface address information to protocol daemon. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) - { - if (ifc->address->family == AF_INET) - if_subnet_add (ifp, ifc); + if (ifc->address->family == AF_INET) + if_subnet_add (ifp, ifc); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + zebra_interface_address_add_update (ifp, ifc); - zebra_interface_address_add_update (ifp, ifc); - - if (if_is_operative(ifp)) - { - if (ifc->address->family == AF_INET) - connected_up_ipv4 (ifp, ifc); + if (if_is_operative(ifp)) + { + if (ifc->address->family == AF_INET) + connected_up_ipv4 (ifp, ifc); #ifdef HAVE_IPV6 - else - connected_up_ipv6 (ifp, ifc); + else + connected_up_ipv6 (ifp, ifc); #endif - } } } - + /* If same interface address is already exist... */ struct connected * connected_check (struct interface *ifp, struct prefix *p) @@ -112,7 +111,7 @@ connected_check (struct interface *ifp, struct prefix *p) return NULL; } -/* Check if two ifc's describe the same address */ +/* Check if two ifc's describe the same address in the same state */ static int connected_same (struct connected *ifc1, struct connected *ifc2) { @@ -132,18 +131,17 @@ connected_same (struct connected *ifc1, struct connected *ifc2) if (ifc1->flags != ifc2->flags) return 0; + + if (ifc1->conf != ifc2->conf) + return 0; return 1; } -/* Handle implicit withdrawals of addresses, where a system ADDs an address - * to an interface which already has the same address configured. - * - * Returns the struct connected which must be announced to clients, - * or NULL if nothing to do. - */ -static struct connected * -connected_implicit_withdraw (struct interface *ifp, struct connected *ifc) +/* Handle changes to addresses and send the neccesary announcements + * to clients. */ +static void +connected_update(struct interface *ifp, struct connected *ifc) { struct connected *current; @@ -156,17 +154,22 @@ connected_implicit_withdraw (struct interface *ifp, struct connected *ifc) /* Avoid spurious withdraws, this might be just the kernel 'reflecting' * back an address we have already added. */ - if (connected_same (current, ifc) && CHECK_FLAG(current->conf, ZEBRA_IFC_REAL)) + if (connected_same (current, ifc)) { /* nothing to do */ connected_free (ifc); - return NULL; + return; } - + + /* Clear the configured flag on the old ifc, so it will be freed by + * connected withdraw. */ UNSET_FLAG(current->conf, ZEBRA_IFC_CONFIGURED); connected_withdraw (current); /* implicit withdraw - freebsd does this */ } - return ifc; + + /* If the connected is new or has changed, announce it, if it is usable */ + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + connected_announce(ifp, ifc); } /* Called from if_up(). */ @@ -210,6 +213,9 @@ connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv4_new (); @@ -272,11 +278,11 @@ connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - /* nothing to do? */ - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + /* For all that I know an IPv4 address is always ready when we receive + * the notification. So it should be safe to set the REAL flag here. */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + + connected_update(ifp, ifc); } void @@ -341,7 +347,7 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) /* Apply mask to the network. */ apply_mask_ipv6 (&p); -#if ! defined (MUSICA) && ! defined (LINUX) +#ifndef LINUX /* XXX: It is already done by rib_bogus_ipv6 within rib_add_ipv6 */ if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; @@ -366,6 +372,9 @@ connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv6_new (); @@ -400,11 +409,16 @@ connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, /* Label of this address. */ if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + + /* On Linux, we only get here when DAD is complete, therefore we can set + * ZEBRA_IFC_REAL. + * + * On BSD, there currently doesn't seem to be a way to check for completion of + * DAD, so we replicate the old behaviour and set ZEBRA_IFC_REAL, although DAD + * might still be running. + */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + connected_update(ifp, ifc); } void diff --git a/zebra/debug.c b/zebra/debug.c index 175029b8..c3b00e0f 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -29,13 +29,14 @@ unsigned long zebra_debug_event; unsigned long zebra_debug_packet; unsigned long zebra_debug_kernel; unsigned long zebra_debug_rib; +unsigned long zebra_debug_fpm; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, "show debugging zebra", SHOW_STR - "Zebra configuration\n" - "Debugging information\n") + "Debugging information\n" + "Zebra configuration\n") { vty_out (vty, "Zebra debugging status:%s", VTY_NEWLINE); @@ -71,6 +72,9 @@ DEFUN (show_debugging_zebra, if (IS_ZEBRA_DEBUG_RIB_Q) vty_out (vty, " Zebra RIB queue debugging is on%s", VTY_NEWLINE); + if (IS_ZEBRA_DEBUG_FPM) + vty_out (vty, " Zebra FPM debugging is on%s", VTY_NEWLINE); + return CMD_SUCCESS; } @@ -124,7 +128,7 @@ DEFUN (debug_zebra_packet_detail, "Debug option set for zebra packet\n" "Debug option set for receive packet\n" "Debug option set for send packet\n" - "Debug option set detaied information\n") + "Debug option set detailed information\n") { zebra_debug_packet = ZEBRA_DEBUG_PACKET; if (strncmp ("send", argv[0], strlen (argv[0])) == 0) @@ -169,6 +173,17 @@ DEFUN (debug_zebra_rib_q, return CMD_SUCCESS; } +DEFUN (debug_zebra_fpm, + debug_zebra_fpm_cmd, + "debug zebra fpm", + DEBUG_STR + "Zebra configuration\n" + "Debug zebra FPM events\n") +{ + SET_FLAG (zebra_debug_fpm, ZEBRA_DEBUG_FPM); + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_events, no_debug_zebra_events_cmd, "no debug zebra events", @@ -247,6 +262,18 @@ DEFUN (no_debug_zebra_rib_q, return CMD_SUCCESS; } +DEFUN (no_debug_zebra_fpm, + no_debug_zebra_fpm_cmd, + "no debug zebra fpm", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra FPM events\n") +{ + zebra_debug_fpm = 0; + return CMD_SUCCESS; +} + /* Debug node. */ struct cmd_node debug_node = { @@ -302,6 +329,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra rib queue%s", VTY_NEWLINE); write++; } + if (IS_ZEBRA_DEBUG_FPM) + { + vty_out (vty, "debug zebra fpm%s", VTY_NEWLINE); + write++; + } return write; } @@ -312,6 +344,7 @@ zebra_debug_init (void) zebra_debug_packet = 0; zebra_debug_kernel = 0; zebra_debug_rib = 0; + zebra_debug_fpm = 0; install_node (&debug_node, config_write_debug); @@ -325,11 +358,13 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &debug_zebra_rib_cmd); install_element (ENABLE_NODE, &debug_zebra_rib_q_cmd); + install_element (ENABLE_NODE, &debug_zebra_fpm_cmd); install_element (ENABLE_NODE, &no_debug_zebra_events_cmd); install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &no_debug_zebra_rib_cmd); install_element (ENABLE_NODE, &no_debug_zebra_rib_q_cmd); + install_element (ENABLE_NODE, &no_debug_zebra_fpm_cmd); install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); @@ -338,9 +373,11 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &debug_zebra_rib_cmd); install_element (CONFIG_NODE, &debug_zebra_rib_q_cmd); + install_element (CONFIG_NODE, &debug_zebra_fpm_cmd); install_element (CONFIG_NODE, &no_debug_zebra_events_cmd); install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &no_debug_zebra_rib_cmd); install_element (CONFIG_NODE, &no_debug_zebra_rib_q_cmd); + install_element (CONFIG_NODE, &no_debug_zebra_fpm_cmd); } diff --git a/zebra/debug.h b/zebra/debug.h index 9d9412bc..d9231a22 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -36,6 +36,8 @@ #define ZEBRA_DEBUG_RIB 0x01 #define ZEBRA_DEBUG_RIB_Q 0x02 +#define ZEBRA_DEBUG_FPM 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -49,10 +51,13 @@ #define IS_ZEBRA_DEBUG_RIB (zebra_debug_rib & ZEBRA_DEBUG_RIB) #define IS_ZEBRA_DEBUG_RIB_Q (zebra_debug_rib & ZEBRA_DEBUG_RIB_Q) +#define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; extern unsigned long zebra_debug_rib; +extern unsigned long zebra_debug_fpm; extern void zebra_debug_init (void); diff --git a/zebra/if_proc.c b/zebra/if_proc.c deleted file mode 100644 index 3aec530b..00000000 --- a/zebra/if_proc.c +++ /dev/null @@ -1,248 +0,0 @@ -/* Interface name and statistics get function using proc file system - * Copyright (C) 1999 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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. - * - * GNU Zebra 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 General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "if.h" -#include "prefix.h" -#include "log.h" - -#include "zebra/ioctl.h" -#include "zebra/connected.h" -#include "zebra/interface.h" - -/* Proc filesystem one line buffer. */ -#define PROCBUFSIZ 1024 - -/* Path to device proc file system. */ -#ifndef _PATH_PROC_NET_DEV -#define _PATH_PROC_NET_DEV "/proc/net/dev" -#endif /* _PATH_PROC_NET_DEV */ - -/* Return statistics data pointer. */ -static char * -interface_name_cut (char *buf, char **name) -{ - char *stat; - - /* Skip white space. Line will include header spaces. */ - while (*buf == ' ') - buf++; - *name = buf; - - /* Cut interface name. */ - stat = strrchr (buf, ':'); - *stat++ = '\0'; - - return stat; -} - -/* Fetch each statistics field. */ -static int -ifstat_dev_fields (int version, char *buf, struct interface *ifp) -{ - switch (version) - { - case 3: - sscanf(buf, - "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_bytes, - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - &ifp->stats.rx_compressed, - &ifp->stats.rx_multicast, - - &ifp->stats.tx_bytes, - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors, - &ifp->stats.tx_compressed); - break; - case 2: - sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_bytes, - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - - &ifp->stats.tx_bytes, - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors); - ifp->stats.rx_multicast = 0; - break; - case 1: - sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors); - ifp->stats.rx_bytes = 0; - ifp->stats.tx_bytes = 0; - ifp->stats.rx_multicast = 0; - break; - } - return 0; -} - -/* Update interface's statistics. */ -void -ifstat_update_proc (void) -{ - FILE *fp; - char buf[PROCBUFSIZ]; - int version; - struct interface *ifp; - char *stat; - char *name; - - /* Open /proc/net/dev. */ - fp = fopen (_PATH_PROC_NET_DEV, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_DEV, safe_strerror (errno)); - return; - } - - /* Drop header lines. */ - fgets (buf, PROCBUFSIZ, fp); - fgets (buf, PROCBUFSIZ, fp); - - /* To detect proc format veresion, parse second line. */ - if (strstr (buf, "compressed")) - version = 3; - else if (strstr (buf, "bytes")) - version = 2; - else - version = 1; - - /* Update each interface's statistics. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - stat = interface_name_cut (buf, &name); - ifp = if_get_by_name (name); - ifstat_dev_fields (version, stat, ifp); - } - fclose(fp); - return; -} - -/* Interface structure allocation by proc filesystem. */ -int -interface_list_proc () -{ - FILE *fp; - char buf[PROCBUFSIZ]; - struct interface *ifp; - char *name; - - /* Open /proc/net/dev. */ - fp = fopen (_PATH_PROC_NET_DEV, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_DEV, safe_strerror (errno)); - return -1; - } - - /* Drop header lines. */ - fgets (buf, PROCBUFSIZ, fp); - fgets (buf, PROCBUFSIZ, fp); - - /* Only allocate interface structure. Other jobs will be done in - if_ioctl.c. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - interface_name_cut (buf, &name); - ifp = if_get_by_name (name); - if_add_update (ifp); - } - fclose(fp); - return 0; -} - -#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) - -#ifndef _PATH_PROC_NET_IF_INET6 -#define _PATH_PROC_NET_IF_INET6 "/proc/net/if_inet6" -#endif /* _PATH_PROC_NET_IF_INET6 */ - -int -ifaddr_proc_ipv6 () -{ - FILE *fp; - char buf[PROCBUFSIZ]; - int n; - char addr[33]; - char ifname[21]; - int ifindex, plen, scope, status; - struct interface *ifp; - struct prefix_ipv6 p; - - /* Open proc file system. */ - fp = fopen (_PATH_PROC_NET_IF_INET6, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_IF_INET6, safe_strerror (errno)); - return -1; - } - - /* Get interface's IPv6 address. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - n = sscanf (buf, "%32s %02x %02x %02x %02x %20s", - addr, &ifindex, &plen, &scope, &status, ifname); - if (n != 6) - continue; - - ifp = if_get_by_name (ifname); - - /* Fetch interface's IPv6 address. */ - str2in6_addr (addr, &p.prefix); - p.prefixlen = plen; - - connected_add_ipv6 (ifp, 0, &p.prefix, p.prefixlen, NULL, ifname); - } - fclose (fp); - return 0; -} -#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ diff --git a/zebra/interface.c b/zebra/interface.c index 3578b790..0271061e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -56,7 +56,7 @@ if_zebra_new_hook (struct interface *ifp) zebra_if = XCALLOC (MTYPE_TMP, sizeof (struct zebra_if)); zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; - zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_UNSPEC; + zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; #ifdef RTADV { @@ -161,11 +161,26 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) /* Get address derived subnet node. */ rn = route_node_lookup (zebra_if->ipv4_subnets, ifc->address); if (! (rn && rn->info)) - return -1; + { + zlog_warn("Trying to remove an address from an unknown subnet." + " (please report this bug)"); + return -1; + } route_unlock_node (rn); /* Untie address from subnet's address list. */ addr_list = rn->info; + + /* Deleting an address that is not registered is a bug. + * In any case, we shouldn't decrement the lock counter if the address + * is unknown. */ + if (!listnode_lookup(addr_list, ifc)) + { + zlog_warn("Trying to remove an address from a subnet where it is not" + " currently registered. (please report this bug)"); + return -1; + } + listnode_delete (addr_list, ifc); route_unlock_node (rn); @@ -178,6 +193,9 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) ifc = listgetdata (listhead (addr_list)); zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY); + /* XXX: Linux kernel removes all the secondary addresses when the primary + * address is removed. We could try to work around that, though this is + * non-trivial. */ zebra_interface_address_add_update (ifp, ifc); } @@ -274,23 +292,30 @@ if_addr_wakeup (struct interface *ifp) p = ifc->address; if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED) - && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)) { /* Address check. */ if (p->family == AF_INET) { if (! if_is_up (ifp)) { - /* XXX: WTF is it trying to set flags here? - * caller has just gotten a new interface, has been - * handed the flags already. This code has no business - * trying to override administrative status of the interface. - * The only call path to here which doesn't originate from - * kernel event is irdp - what on earth is it trying to do? - * - * further RUNNING is not a settable flag on any system - * I (paulj) am aware of. - */ + /* Assume zebra is configured like following: + * + * interface gre0 + * ip addr 192.0.2.1/24 + * ! + * + * As soon as zebra becomes first aware that gre0 exists in the + * kernel, it will set gre0 up and configure its addresses. + * + * (This may happen at startup when the interface already exists + * or during runtime when the interface is added to the kernel) + * + * XXX: IRDP code is calling here via if_add_update - this seems + * somewhat weird. + * XXX: RUNNING is not a settable flag on any system + * I (paulj) am aware of. + */ if_set_flags (ifp, IFF_UP | IFF_RUNNING); if_refresh (ifp); } @@ -303,22 +328,17 @@ if_addr_wakeup (struct interface *ifp) continue; } - /* Add to subnet chain list. */ - if_subnet_add (ifp, ifc); - - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - zebra_interface_address_add_update (ifp, ifc); - - if (if_is_operative(ifp)) - connected_up_ipv4 (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. + * It will also be added to the interface's subnet list then. */ } #ifdef HAVE_IPV6 if (p->family == AF_INET6) { if (! if_is_up (ifp)) { - /* XXX: See long comment above */ + /* See long comment above */ if_set_flags (ifp, IFF_UP | IFF_RUNNING); if_refresh (ifp); } @@ -330,12 +350,10 @@ if_addr_wakeup (struct interface *ifp) safe_strerror(errno)); continue; } - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - zebra_interface_address_add_update (ifp, ifc); - if (if_is_operative(ifp)) - connected_up_ipv6 (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. */ } #endif /* HAVE_IPV6 */ } @@ -360,6 +378,14 @@ if_add_update (struct interface *ifp) { SET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); + if (if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("interface %s index %d is shutdown. Won't wake it up.", + ifp->name, ifp->ifindex); + return; + } + if_addr_wakeup (ifp); if (IS_ZEBRA_DEBUG_KERNEL) @@ -433,12 +459,15 @@ if_delete_update (struct interface *ifp) ifc = listgetdata (anode); p = ifc->address; - connected_down_ipv4 (ifp, ifc); + /* XXX: We have to send notifications here explicitly, because we destroy + * the ifc before receiving the notification about the address being deleted. + */ zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); /* Remove from subnet chain. */ list_delete_node (addr_list, anode); @@ -467,6 +496,7 @@ if_delete_update (struct interface *ifp) zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) last = node; @@ -1073,13 +1103,16 @@ DEFUN (shutdown_if, struct zebra_if *if_data; ifp = (struct interface *) vty->index; - ret = if_unset_flags (ifp, IFF_UP); - if (ret < 0) + if (ifp->ifindex != IFINDEX_INTERNAL) { - vty_out (vty, "Can't shutdown interface%s", VTY_NEWLINE); - return CMD_WARNING; + ret = if_unset_flags (ifp, IFF_UP); + if (ret < 0) + { + vty_out (vty, "Can't shutdown interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); } - if_refresh (ifp); if_data = ifp->info; if_data->shutdown = IF_ZEBRA_SHUTDOWN_ON; @@ -1097,13 +1130,23 @@ DEFUN (no_shutdown_if, struct zebra_if *if_data; ifp = (struct interface *) vty->index; - ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING); - if (ret < 0) + + if (ifp->ifindex != IFINDEX_INTERNAL) { - vty_out (vty, "Can't up interface%s", VTY_NEWLINE); - return CMD_WARNING; + ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING); + if (ret < 0) + { + vty_out (vty, "Can't up interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); + + /* Some addresses (in particular, IPv6 addresses on Linux) get + * removed when the interface goes down. They need to be readded. + */ + if_addr_wakeup(ifp); } - if_refresh (ifp); + if_data = ifp->info; if_data->shutdown = IF_ZEBRA_SHUTDOWN_OFF; @@ -1163,17 +1206,20 @@ ALIAS (no_bandwidth_if, NO_STR "Set bandwidth informational parameter\n" "Bandwidth in kilobits\n") - + static int ip_address_install (struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) { + struct zebra_if *if_data; struct prefix_ipv4 cp; struct connected *ifc; struct prefix_ipv4 *p; int ret; + if_data = ifp->info; + ret = str2prefix_ipv4 (addr_str, &cp); if (ret <= 0) { @@ -1214,8 +1260,9 @@ ip_address_install (struct vty *vty, struct interface *ifp, SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* In case of this route need to install kernel. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) - && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) + && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { /* Some system need to up the interface to set IP address. */ if (! if_is_up (ifp)) @@ -1232,18 +1279,10 @@ ip_address_install (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* Add to subnet chain list (while marking secondary attribute). */ - if_subnet_add (ifp, ifc); - - /* IP address propery set. */ - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* Update interface address information to protocol daemon. */ - zebra_interface_address_add_update (ifp, ifc); - - /* If interface is up register connected route. */ - if (if_is_operative(ifp)) - connected_up_ipv4 (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. + * It will also be added to the subnet chain list, then. */ } return CMD_SUCCESS; @@ -1281,7 +1320,7 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* This is not real address or interface is not active. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete (ifp->connected, ifc); @@ -1297,34 +1336,9 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } - /* success! call returned that the address deletion went through. - * this is a synchronous operation, so we know it succeeded and can - * now update all internal state. */ - - /* the HAVE_NETLINK check is only here because, on BSD, although the - * call above is still synchronous, we get a second confirmation later - * through the route socket, and we don't want to touch that behaviour - * for now. It should work without the #ifdef, but why take the risk... - * -- equinox 2012-07-13 */ -#ifdef HAVE_NETLINK - - /* Remove connected route. */ - connected_down_ipv4 (ifp, ifc); - - /* Redistribute this information. */ - zebra_interface_address_delete_update (ifp, ifc); - - /* IP address propery set. */ - UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* remove from interface, remark secondaries */ - if_subnet_delete (ifp, ifc); - - /* Free address information. */ - listnode_delete (ifp->connected, ifc); - connected_free (ifc); -#endif - + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* we will receive a kernel notification about this route being removed. + * this will trigger its removal from the connected list. */ return CMD_SUCCESS; } @@ -1382,11 +1396,14 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label, int secondary) { + struct zebra_if *if_data; struct prefix_ipv6 cp; struct connected *ifc; struct prefix_ipv6 *p; int ret; + if_data = ifp->info; + ret = str2prefix_ipv6 (addr_str, &cp); if (ret <= 0) { @@ -1422,8 +1439,9 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* In case of this route need to install kernel. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) - && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) + && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { /* Some system need to up the interface to set IP address. */ if (! if_is_up (ifp)) @@ -1441,15 +1459,9 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* IP address propery set. */ - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* Update interface address information to protocol daemon. */ - zebra_interface_address_add_update (ifp, ifc); - - /* If interface is up register connected route. */ - if (if_is_operative(ifp)) - connected_up_ipv6 (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. */ } return CMD_SUCCESS; @@ -1484,8 +1496,10 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) return CMD_WARNING; + UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); + /* This is not real address or interface is not active. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete (ifp->connected, ifc); @@ -1502,16 +1516,9 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* Redistribute this information. */ - zebra_interface_address_delete_update (ifp, ifc); - - /* Remove connected route. */ - connected_down_ipv6 (ifp, ifc); - - /* Free address information. */ - listnode_delete (ifp->connected, ifc); - connected_free (ifc); - + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* This information will be propagated to the zclients when the + * kernel notification is received. */ return CMD_SUCCESS; } @@ -1555,6 +1562,12 @@ if_config_write (struct vty *vty) vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); + if (if_data) + { + if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) + vty_out (vty, " shutdown%s", VTY_NEWLINE); + } + if (ifp->desc) vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE); @@ -1566,6 +1579,8 @@ if_config_write (struct vty *vty) if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) vty_out(vty, " link-detect%s", VTY_NEWLINE); + else + vty_out(vty, " no link-detect%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (ifp->connected, addrnode, ifc)) { @@ -1587,9 +1602,6 @@ if_config_write (struct vty *vty) if (if_data) { - if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) - vty_out (vty, " shutdown%s", VTY_NEWLINE); - if (if_data->multicast != IF_ZEBRA_MULTICAST_UNSPEC) vty_out (vty, " %smulticast%s", if_data->multicast == IF_ZEBRA_MULTICAST_ON ? "" : "no ", diff --git a/zebra/interface.h b/zebra/interface.h index 0777a2fa..2f3b7b93 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -34,9 +34,8 @@ #define IF_ZEBRA_MULTICAST_OFF 2 /* For interface shutdown configuration. */ -#define IF_ZEBRA_SHUTDOWN_UNSPEC 0 +#define IF_ZEBRA_SHUTDOWN_OFF 0 #define IF_ZEBRA_SHUTDOWN_ON 1 -#define IF_ZEBRA_SHUTDOWN_OFF 2 /* Router advertisement feature. */ #if (defined(LINUX_IPV6) && (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1)) || defined(KAME) @@ -237,8 +236,4 @@ extern int interface_list_proc (void); extern int ifaddr_proc_ipv6 (void); #endif /* HAVE_PROC_NET_IF_INET6 */ -#ifdef BSDI -extern int if_kvm_get_mtu (struct interface *); -#endif /* BSDI */ - #endif /* _ZEBRA_INTERFACE_H */ diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c index 6d8e13a0..5a8be991 100644 --- a/zebra/ioctl_null.c +++ b/zebra/ioctl_null.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "zebra/rib.h" @@ -19,16 +40,29 @@ int if_unset_prefix (struct interface *a, struct connected *b) } int if_prefix_add_ipv6 (struct interface *a, struct connected *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_prefix_delete_ipv6 = if_prefix_add_ipv6 +#else +int if_prefix_delete_ipv6 (struct interface *a, struct connected *b) { return 0; } +#endif int if_ioctl (u_long a, caddr_t b) { return 0; } int if_set_flags (struct interface *a, uint64_t b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_unset_flags = if_set_flags +#else +int if_unset_flags (struct interface *a, uint64_t b) { return 0; } +#endif void if_get_flags (struct interface *a) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_get_metric = if_get_flags #pragma weak if_get_mtu = if_get_flags +#else +/* void if_get_metric (struct interface *a) { return; } */ +/* void if_get_mtu (struct interface *a) { return; } */ +#endif #ifdef SOLARIS_IPV6 #pragma weak if_ioctl_ipv6 = if_ioctl diff --git a/zebra/ipforward_ews.c b/zebra/ipforward_ews.c deleted file mode 100644 index c872000a..00000000 --- a/zebra/ipforward_ews.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Ipforward value get function for NEC EWS. - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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. - * - * GNU Zebra 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 General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -int -ipforward () -{ - int fd; - char buf[BUFSIZ]; - struct mioc_rksym rks; - - fd = open ("/dev/kmem", O_RDWR); - if (fd < 0) { - /* need logging here */ - return -1; - } - - rks.mirk_symname = "ipforwarding"; - rks.mirk_buf = buf; - rks.mirk_buflen = sizeof (int); - - if (ioctl (fd, MIOC_READKSYM, &rks) < 0) { - /* need logging here */ - return -1; - } - close (fd); - return *(int *)buf; -} - -int -ipforward_on () -{ - ; -} - -int -ipforward_off () -{ - ; -} diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index 185aee3e..57ed1857 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -23,10 +23,6 @@ #include "privs.h" #include "zebra/ipforward.h" -#ifdef NRL -#include -#endif /* NRL */ - #include "log.h" #define MIB_SIZ 4 @@ -106,7 +102,7 @@ int mib_ipv6[MIB_SIZ] = { CTL_NET, PF_INET6, -#if defined(KAME) || (defined(__bsdi__) && _BSDI_VERSION >= 199802 ) || defined(NRL) +#if defined(KAME) IPPROTO_IPV6, IPV6CTL_FORWARDING #else /* NOT KAME */ diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 6403830b..43c63a83 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -638,7 +638,6 @@ DEFUN (no_ip_irdp_address_preference, { struct listnode *node, *nnode; struct in_addr ip; - int pref; int ret; struct interface *ifp; struct zebra_if *zi; @@ -657,8 +656,6 @@ DEFUN (no_ip_irdp_address_preference, if (!ret) return CMD_WARNING; - pref = atoi(argv[1]); - for (ALL_LIST_ELEMENTS (irdp->AdvPrefList, node, nnode, adv)) { if(adv->ip.s_addr == ip.s_addr ) diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 28dc171e..0d31050c 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -184,7 +184,7 @@ parse_irdp_packet(char *p, inet_ntoa (src)); } } - + static int irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex) { @@ -222,7 +222,7 @@ irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex) return ret; } - + int irdp_read_raw(struct thread *r) { struct interface *ifp; @@ -264,7 +264,7 @@ int irdp_read_raw(struct thread *r) return ret; } - + void send_packet(struct interface *ifp, struct stream *s, @@ -287,7 +287,7 @@ send_packet(struct interface *ifp, if (!(ifp->flags & IFF_UP)) return; - if (!p) + if (p) src = ntohl(p->u.prefix4.s_addr); else src = 0; /* Is filled in */ diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 6b96c6df..4cd43db4 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -1,5 +1,26 @@ /* NULL kernel methods for testing. */ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include @@ -9,12 +30,18 @@ #include "zebra/connected.h" int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak kernel_delete_ipv4 = kernel_add_ipv4 +#else +int kernel_delete_ipv4 (struct prefix *a, struct rib *b) { return 0; } +#endif + int kernel_add_ipv6 (struct prefix *a, struct rib *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak kernel_delete_ipv6 = kernel_add_ipv6 -int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ return 0; } +#else +int kernel_delete_ipv6 (struct prefix *a, struct rib *b) { return 0; } +#endif int kernel_add_route (struct prefix_ipv4 *a, struct in_addr *b, int c, int d) { return 0; } @@ -38,4 +65,8 @@ int kernel_address_delete_ipv4 (struct interface *a, struct connected *b) } void kernel_init (void) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak route_read = kernel_init +#else +void route_read (void) { return; } +#endif diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index cde36bd0..1518c1ab 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -42,22 +42,52 @@ extern struct zebra_privs_t zserv_privs; extern struct zebra_t zebrad; /* - * Given a sockaddr length, round it up to include pad bytes following - * it. Assumes the kernel pads to sizeof(long). + * Historically, the BSD routing socket has aligned data following a + * struct sockaddr to sizeof(long), which was 4 bytes on some + * platforms, and 8 bytes on others. NetBSD 6 changed the routing + * socket to align to sizeof(uint64_t), which is 8 bytes. OS X + * appears to align to sizeof(int), which is 4 bytes. * - * XXX: why is ROUNDUP(0) sizeof(long)? 0 is an illegal sockaddr - * length anyway (< sizeof (struct sockaddr)), so this shouldn't - * matter. - * On OS X, both 32, 64bit syatems align on 4 byte boundary + * Alignment of zero-sized sockaddrs is nonsensical, but historically + * BSD defines RT_ROUNDUP(0) to be the alignment interval (rather than + * 0). We follow this practice without questioning it, but it is a + * bug if quagga calls ROUNDUP with 0. */ + +/* + * Because of these varying conventions, the only sane approach is for + * the header to define some flavor of ROUNDUP macro. + */ +#if defined(RT_ROUNDUP) +#define ROUNDUP(a) RT_ROUNDUP(a) +#endif /* defined(RT_ROUNDUP) */ + +/* + * If ROUNDUP has not yet been defined in terms of platform-provided + * defines, attempt to cope with heuristics. + */ +#if !defined(ROUNDUP) + +/* + * It's a bug for a platform not to define rounding/alignment for + * sockaddrs on the routing socket. This warning really is + * intentional, to provoke filing bug reports with operating systems + * that don't define RT_ROUNDUP or equivalent. + */ +#warning "net/route.h does not define RT_ROUNDUP; making unwarranted assumptions!" + +/* OS X (Xcode as of 2014-12) is known not to define RT_ROUNDUP */ #ifdef __APPLE__ -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int) - 1))) : sizeof(int)) +#define ROUNDUP_TYPE long #else -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ROUNDUP_TYPE int #endif +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(ROUNDUP_TYPE) - 1))) : sizeof(ROUNDUP_TYPE)) + +#endif /* defined(ROUNDUP) */ + /* * Given a pointer (sockaddr or void *), return the number of bytes * taken up by the sockaddr and any padding needed for alignment. @@ -252,7 +282,7 @@ af_check (int family) #endif /* HAVE_IPV6 */ return 0; } - + /* Dump routing table flag for debug purpose. */ static void rtm_flag_dump (int flag) @@ -299,6 +329,7 @@ ifan_read (struct if_announcemsghdr *ifan) sizeof(ifan->ifan_name))); ifp->ifindex = ifan->ifan_index; + if_get_metric (ifp); if_add_update (ifp); } else if (ifp != NULL && ifan->ifan_what == IFAN_DEPARTURE) @@ -477,13 +508,22 @@ ifm_read (struct if_msghdr *ifm) /* * XXX sockaddr_dl contents can be larger than the structure - * definition, so the user of the stored structure must be - * careful not to read off the end. - * + * definition. There are 2 big families here: + * - BSD has sdl_len + sdl_data[16] + overruns sdl_data + * we MUST use sdl_len here or we'll truncate data. + * - Solaris has no sdl_len, but sdl_data[244] + * presumably, it's not going to run past that, so sizeof() + * is fine here. * a nonzero ifnlen from RTA_NAME_GET() means sdl is valid */ if (ifnlen) + { +#ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN + memcpy (&ifp->sdl, sdl, sdl->sdl_len); +#else memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl)); +#endif /* HAVE_STRUCT_SOCKADDR_DL_SDL_LEN */ + } if_add_update (ifp); } @@ -547,7 +587,7 @@ ifm_read (struct if_msghdr *ifm) return 0; } - + /* Address read from struct ifa_msghdr. */ static void ifam_read_mesg (struct ifa_msghdr *ifm, @@ -741,7 +781,7 @@ ifam_read (struct ifa_msghdr *ifam) return 0; } - + /* Interface function for reading kernel routing table information. */ static int rtm_read_mesg (struct rt_msghdr *rtm, @@ -1118,7 +1158,7 @@ rtm_write (int message, return ZEBRA_ERR_NOERROR; } - + #include "thread.h" #include "zebra/zserv.h" diff --git a/zebra/main.c b/zebra/main.c index 50ac224e..d7f2a108 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -39,6 +39,7 @@ #include "zebra/router-id.h" #include "zebra/irdp.h" #include "zebra/rtadv.h" +#include "zebra/zebra_fpm.h" /* Zebra instance */ struct zebra_t zebrad = @@ -150,7 +151,7 @@ usage (char *progname, int status) exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -202,7 +203,7 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + /* Main startup routine. */ int main (int argc, char **argv) @@ -342,13 +343,16 @@ main (int argc, char **argv) interface_list (); route_read (); - /* Sort VTY commands. */ - sort_node (); - #ifdef HAVE_SNMP zebra_snmp_init (); #endif /* HAVE_SNMP */ +#ifdef HAVE_FPM + zfpm_init (zebrad.master, 1, 0); +#else + zfpm_init (zebrad.master, 0, 0); +#endif + /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such * requests will not be executed immediately, but queued in diff --git a/zebra/misc_null.c b/zebra/misc_null.c index 73594301..b4416e63 100644 --- a/zebra/misc_null.c +++ b/zebra/misc_null.c @@ -1,11 +1,45 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "prefix.h" #include "zebra/rtadv.h" #include "zebra/irdp.h" #include "zebra/interface.h" +#include "zebra/zebra_fpm.h" void ifstat_update_proc (void) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak rtadv_config_write = ifstat_update_proc #pragma weak irdp_config_write = ifstat_update_proc #pragma weak ifstat_update_sysctl = ifstat_update_proc +#else +void rtadv_config_write (struct vty *vty, struct interface *ifp) { return; } +void irdp_config_write (struct vty *vty, struct interface *ifp) { return; } +void ifstat_update_sysctl (void) { return; } +#endif + +void +zfpm_trigger_update (struct route_node *rn, const char *reason) +{ + return; +} diff --git a/zebra/mtu_kvm.c b/zebra/mtu_kvm.c deleted file mode 100644 index d37bb9bb..00000000 --- a/zebra/mtu_kvm.c +++ /dev/null @@ -1,97 +0,0 @@ -/* MTU get using kvm_read. - * Copyright (C) 1999 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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. - * - * GNU Zebra 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 General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include -#include -#include - -#include "if.h" - -/* get interface MTU to use kvm_read */ -void -if_kvm_get_mtu (struct interface *ifp) -{ - kvm_t *kvmd; - struct ifnet ifnet; - unsigned long ifnetaddr; - int len; - - char ifname[IFNAMSIZ]; - char tname[INTERFACE_NAMSIZ + 1]; - char buf[_POSIX2_LINE_MAX]; - - struct nlist nl[] = - { - {"_ifnet"}, - {""} - }; - - ifp->mtu6 = ifp->mtu = -1; - - kvmd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, buf); - - if (kvmd == NULL) - return ; - - kvm_nlist(kvmd, nl); - - ifnetaddr = nl[0].n_value; - - if (kvm_read(kvmd, ifnetaddr, (char *)&ifnetaddr, sizeof ifnetaddr) < 0) - { - kvm_close (kvmd); - return ; - } - - while(ifnetaddr != 0) - { - if (kvm_read (kvmd, ifnetaddr, (char *)&ifnet, sizeof ifnet) < 0) - { - kvm_close (kvmd); - return ; - } - - if (kvm_read (kvmd, (u_long)ifnet.if_name, ifname, IFNAMSIZ) < 0) - { - kvm_close (kvmd); - return ; - } - - len = snprintf (tname, INTERFACE_NAMSIZ + 1, - "%s%d", ifname, ifnet.if_unit); - - if (strncmp (tname, ifp->name, len) == 0) - break; - - ifnetaddr = (u_long)ifnet.if_next; - } - - kvm_close (kvmd); - - if (ifnetaddr == 0) - { - return ; - } - - ifp->mtu6 = ifp->mtu = ifnet.if_mtu; -} diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 4276f1d0..078c2ad0 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -107,7 +107,7 @@ zebra_redistribute_default (struct zserv *client) rn = route_node_lookup (table, (struct prefix *)&p); if (rn) { - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->distance != DISTANCE_INFINITY) zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib); @@ -127,7 +127,7 @@ zebra_redistribute_default (struct zserv *client) rn = route_node_lookup (table, (struct prefix *)&p6); if (rn) { - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->distance != DISTANCE_INFINITY) zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib); @@ -148,7 +148,7 @@ zebra_redistribute (struct zserv *client, int type) table = vrf_table (AFI_IP, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->type == type && newrib->distance != DISTANCE_INFINITY @@ -159,7 +159,7 @@ zebra_redistribute (struct zserv *client, int type) table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->type == type && newrib->distance != DISTANCE_INFINITY @@ -358,6 +358,9 @@ zebra_interface_address_add_update (struct interface *ifp, p->prefixlen, ifc->ifp->name); } + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + zlog_warn("WARNING: advertising address to clients that is not yet usable."); + router_id_add_address(ifc); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c index e57a73b9..c45ebe1d 100644 --- a/zebra/redistribute_null.c +++ b/zebra/redistribute_null.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "zebra/rib.h" #include "zebra/zserv.h" @@ -6,21 +27,50 @@ void zebra_redistribute_add (int a, struct zserv *b, int c) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_redistribute_delete = zebra_redistribute_add #pragma weak zebra_redistribute_default_add = zebra_redistribute_add #pragma weak zebra_redistribute_default_delete = zebra_redistribute_add +#else +void zebra_redistribute_delete (int a, struct zserv *b, int c) +{ return; } +void zebra_redistribute_default_add (int a, struct zserv *b, int c) +{ return; } +void zebra_redistribute_default_delete (int a, struct zserv *b, int c) +{ return; } +#endif void redistribute_add (struct prefix *a, struct rib *b) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak redistribute_delete = redistribute_add +#else +void redistribute_delete (struct prefix *a, struct rib *b) +{ return; } +#endif void zebra_interface_up_update (struct interface *a) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_interface_down_update = zebra_interface_up_update #pragma weak zebra_interface_add_update = zebra_interface_up_update #pragma weak zebra_interface_delete_update = zebra_interface_up_update +#else +void zebra_interface_down_update (struct interface *a) +{ return; } +void zebra_interface_add_update (struct interface *a) +{ return; } +void zebra_interface_delete_update (struct interface *a) +{ return; } +#endif void zebra_interface_address_add_update (struct interface *a, struct connected *b) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_interface_address_delete_update = zebra_interface_address_add_update +#else +void zebra_interface_address_delete_update (struct interface *a, + struct connected *b) +{ return; } +#endif diff --git a/zebra/rib.h b/zebra/rib.h index 1b85c81e..94a74194 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,6 +24,8 @@ #define _ZEBRA_RIB_H #include "prefix.h" +#include "table.h" +#include "queue.h" #define DISTANCE_INFINITY 255 @@ -38,10 +40,6 @@ union g_addr { struct rib { - /* Status Flags for the *route_node*, but kept in the head RIB.. */ - u_char rn_status; -#define RIB_ROUTE_QUEUED(x) (1 << (x)) - /* Link list. */ struct rib *next; struct rib *prev; @@ -97,6 +95,74 @@ struct meta_queue u_int32_t size; /* sum of lengths of all subqueues */ }; +/* + * Structure that represents a single destination (prefix). + */ +typedef struct rib_dest_t_ +{ + + /* + * Back pointer to the route node for this destination. This helps + * us get to the prefix that this structure is for. + */ + struct route_node *rnode; + + /* + * Doubly-linked list of routes for this prefix. + */ + struct rib *routes; + + /* + * Flags, see below. + */ + u_int32_t flags; + + /* + * Linkage to put dest on the FPM processing queue. + */ + TAILQ_ENTRY(rib_dest_t_) fpm_q_entries; + +} rib_dest_t; + +#define RIB_ROUTE_QUEUED(x) (1 << (x)) + +/* + * The maximum qindex that can be used. + */ +#define ZEBRA_MAX_QINDEX (MQ_SIZE - 1) + +/* + * This flag indicates that a given prefix has been 'advertised' to + * the FPM to be installed in the forwarding plane. + */ +#define RIB_DEST_SENT_TO_FPM (1 << (ZEBRA_MAX_QINDEX + 1)) + +/* + * This flag is set when we need to send an update to the FPM about a + * dest. + */ +#define RIB_DEST_UPDATE_FPM (1 << (ZEBRA_MAX_QINDEX + 2)) + +/* + * Macro to iterate over each route for a destination (prefix). + */ +#define RIB_DEST_FOREACH_ROUTE(dest, rib) \ + for ((rib) = (dest) ? (dest)->routes : NULL; (rib); (rib) = (rib)->next) + +/* + * Same as above, but allows the current node to be unlinked. + */ +#define RIB_DEST_FOREACH_ROUTE_SAFE(dest, rib, next) \ + for ((rib) = (dest) ? (dest)->routes : NULL; \ + (rib) && ((next) = (rib)->next, 1); \ + (rib) = (next)) + +#define RNODE_FOREACH_RIB(rn, rib) \ + RIB_DEST_FOREACH_ROUTE (rib_dest_from_rnode (rn), rib) + +#define RNODE_FOREACH_RIB_SAFE(rn, rib, next) \ + RIB_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode (rn), rib, next) + /* Static route information. */ struct static_ipv4 { @@ -187,17 +253,67 @@ struct nexthop #define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */ #define NEXTHOP_FLAG_FIB (1 << 1) /* FIB nexthop. */ #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ +#define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */ - /* Nexthop address or interface name. */ + /* Nexthop address */ union g_addr gate; - - /* Recursive lookup nexthop. */ - u_char rtype; - unsigned int rifindex; - union g_addr rgate; union g_addr src; + + /* Nexthops obtained by recursive resolution. + * + * If the nexthop struct needs to be resolved recursively, + * NEXTHOP_FLAG_RECURSIVE will be set in flags and the nexthops + * obtained by recursive resolution will be added to `resolved'. + * Only one level of recursive resolution is currently supported. */ + struct nexthop *resolved; }; +/* The following for loop allows to iterate over the nexthop + * structure of routes. + * + * We have to maintain quite a bit of state: + * + * nexthop: The pointer to the current nexthop, either in the + * top-level chain or in the resolved chain of ni. + * tnexthop: The pointer to the current nexthop in the top-level + * nexthop chain. + * recursing: Information if nh currently is in the top-level chain + * (0) or in a resolved chain (1). + * + * Initialization: Set `nexthop' and `tnexthop' to the head of the + * top-level chain. As nexthop is in the top level chain, set recursing + * to 0. + * + * Iteration check: Check that the `nexthop' pointer is not NULL. + * + * Iteration step: This is the tricky part. Check if `nexthop' has + * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' is in + * the top level chain and has at least one nexthop attached to + * `nexthop->resolved'. As we want to descend into `nexthop->resolved', + * set `recursing' to 1 and set `nexthop' to `nexthop->resolved'. + * `tnexthop' is left alone in that case so we can remember which nexthop + * in the top level chain we are currently handling. + * + * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its + * current chain. If we are recursing, `nexthop' will be set to + * `nexthop->next' and `tnexthop' will be left alone. If we are not + * recursing, both `tnexthop' and `nexthop' will be set to `nexthop->next' + * as we are progressing in the top level chain. + * If we encounter `nexthop->next == NULL', we will clear the `recursing' + * flag as we arived either at the end of the resolved chain or at the end + * of the top level chain. In both cases, we set `tnexthop' and `nexthop' + * to `tnexthop->next', progressing to the next position in the top-level + * chain and possibly to its end marked by NULL. + */ +#define ALL_NEXTHOPS_RO(head, nexthop, tnexthop, recursing) \ + (tnexthop) = (nexthop) = (head), (recursing) = 0; \ + (nexthop); \ + (nexthop) = CHECK_FLAG((nexthop)->flags, NEXTHOP_FLAG_RECURSIVE) \ + ? (((recursing) = 1), (nexthop)->resolved) \ + : ((nexthop)->next ? ((recursing) ? (nexthop)->next \ + : ((tnexthop) = (nexthop)->next)) \ + : (((recursing) = 0),((tnexthop) = (tnexthop)->next))) + /* Routing table instance. */ struct vrf { @@ -220,6 +336,59 @@ struct vrf struct route_table *stable[AFI_MAX][SAFI_MAX]; }; +/* + * rib_table_info_t + * + * Structure that is hung off of a route_table that holds information about + * the table. + */ +typedef struct rib_table_info_t_ +{ + + /* + * Back pointer to vrf. + */ + struct vrf *vrf; + afi_t afi; + safi_t safi; + +} rib_table_info_t; + +typedef enum +{ + RIB_TABLES_ITER_S_INIT, + RIB_TABLES_ITER_S_ITERATING, + RIB_TABLES_ITER_S_DONE +} rib_tables_iter_state_t; + +/* + * Structure that holds state for iterating over all tables in the + * Routing Information Base. + */ +typedef struct rib_tables_iter_t_ +{ + uint32_t vrf_id; + int afi_safi_ix; + + rib_tables_iter_state_t state; +} rib_tables_iter_t; + +/* RPF lookup behaviour */ +enum multicast_mode +{ + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + +extern void multicast_mode_ipv4_set (enum multicast_mode mode); +extern enum multicast_mode multicast_mode_ipv4_get (void); + +extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); extern struct nexthop *nexthop_blackhole_add (struct rib *); @@ -229,9 +398,12 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, struct in_addr *, struct in_addr *, unsigned int); +extern int nexthop_has_fib_child(struct nexthop *); extern void rib_lookup_and_dump (struct prefix_ipv4 *); extern void rib_lookup_and_pushup (struct prefix_ipv4 *); -extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *); +#define rib_dump(prefix ,rib) _rib_dump(__func__, prefix, rib) +extern void _rib_dump (const char *, + union prefix46constptr, const struct rib *); extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *); #define ZEBRA_RIB_LOOKUP_ERROR -1 #define ZEBRA_RIB_FOUND_EXACT 0 @@ -261,7 +433,10 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, u_int32_t, safi_t safi); -extern struct rib *rib_match_ipv4 (struct in_addr); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, + int skip_bgp, struct route_node **rn_out); +extern struct rib *rib_match_ipv4_multicast (struct in_addr addr, + struct route_node **rn_out); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); @@ -273,12 +448,12 @@ extern void rib_init (void); extern unsigned long rib_score_proto (u_char proto); extern int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id); - +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id); extern int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id); +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id); #ifdef HAVE_IPV6 extern int @@ -307,4 +482,117 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, #endif /* HAVE_IPV6 */ +extern int rib_gc_dest (struct route_node *rn); +extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter); + +/* + * Inline functions. + */ + +/* + * rib_table_info + */ +static inline rib_table_info_t * +rib_table_info (struct route_table *table) +{ + return (rib_table_info_t *) table->info; +} + +/* + * rib_dest_from_rnode + */ +static inline rib_dest_t * +rib_dest_from_rnode (struct route_node *rn) +{ + return (rib_dest_t *) rn->info; +} + +/* + * rnode_to_ribs + * + * Returns a pointer to the list of routes corresponding to the given + * route_node. + */ +static inline struct rib * +rnode_to_ribs (struct route_node *rn) +{ + rib_dest_t *dest; + + dest = rib_dest_from_rnode (rn); + if (!dest) + return NULL; + + return dest->routes; +} + +/* + * rib_dest_prefix + */ +static inline struct prefix * +rib_dest_prefix (rib_dest_t *dest) +{ + return &dest->rnode->p; +} + +/* + * rib_dest_af + * + * Returns the address family that the destination is for. + */ +static inline u_char +rib_dest_af (rib_dest_t *dest) +{ + return dest->rnode->p.family; +} + +/* + * rib_dest_table + */ +static inline struct route_table * +rib_dest_table (rib_dest_t *dest) +{ + return dest->rnode->table; +} + +/* + * rib_dest_vrf + */ +static inline struct vrf * +rib_dest_vrf (rib_dest_t *dest) +{ + return rib_table_info (rib_dest_table (dest))->vrf; +} + +/* + * rib_tables_iter_init + */ +static inline void +rib_tables_iter_init (rib_tables_iter_t *iter) + +{ + memset (iter, 0, sizeof (*iter)); + iter->state = RIB_TABLES_ITER_S_INIT; +} + +/* + * rib_tables_iter_started + * + * Returns TRUE if this iterator has started iterating over the set of + * tables. + */ +static inline int +rib_tables_iter_started (rib_tables_iter_t *iter) +{ + return iter->state != RIB_TABLES_ITER_S_INIT; +} + +/* + * rib_tables_iter_cleanup + */ +static inline void +rib_tables_iter_cleanup (rib_tables_iter_t *iter) +{ + iter->state = RIB_TABLES_ITER_S_DONE; +} + #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/router-id.c b/zebra/router-id.c index 3d6b511c..94a29419 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -41,8 +41,13 @@ #include "zebra/router-id.h" #include "zebra/redistribute.h" -static struct list rid_all_sorted_list; -static struct list rid_lo_sorted_list; +/* 2nd pointer type used primarily to quell a warning on + * ALL_LIST_ELEMENTS_RO + */ +static struct list _rid_all_sorted_list; +static struct list _rid_lo_sorted_list; +static struct list *rid_all_sorted_list = &_rid_all_sorted_list; +static struct list *rid_lo_sorted_list = &_rid_lo_sorted_list; static struct prefix rid_user_assigned; /* master zebra server structure */ @@ -86,15 +91,15 @@ router_id_get (struct prefix *p) if (rid_user_assigned.u.prefix4.s_addr) p->u.prefix4.s_addr = rid_user_assigned.u.prefix4.s_addr; - else if (!list_isempty (&rid_lo_sorted_list)) + else if (!list_isempty (rid_lo_sorted_list)) { - node = listtail (&rid_lo_sorted_list); + node = listtail (rid_lo_sorted_list); c = listgetdata (node); p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; } - else if (!list_isempty (&rid_all_sorted_list)) + else if (!list_isempty (rid_all_sorted_list)) { - node = listtail (&rid_all_sorted_list); + node = listtail (rid_all_sorted_list); c = listgetdata (node); p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; } @@ -131,9 +136,9 @@ router_id_add_address (struct connected *ifc) if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = rid_all_sorted_list; if (!router_id_find_node (l, ifc)) listnode_add_sort (l, ifc); @@ -164,9 +169,9 @@ router_id_del_address (struct connected *ifc) if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = rid_all_sorted_list; if ((c = router_id_find_node (l, ifc))) listnode_delete (l, c); @@ -230,10 +235,8 @@ router_id_cmp (void *a, void *b) { const struct connected *ifa = (const struct connected *)a; const struct connected *ifb = (const struct connected *)b; - unsigned int A = ntohl(ifa->address->u.prefix4.s_addr); - unsigned int B = ntohl(ifb->address->u.prefix4.s_addr); - return (int) (A - B); + return IPV4_ADDR_CMP(&ifa->address->u.prefix4.s_addr,&ifb->address->u.prefix4.s_addr); } void @@ -242,12 +245,12 @@ router_id_init (void) install_element (CONFIG_NODE, &router_id_cmd); install_element (CONFIG_NODE, &no_router_id_cmd); - memset (&rid_all_sorted_list, 0, sizeof (rid_all_sorted_list)); - memset (&rid_lo_sorted_list, 0, sizeof (rid_lo_sorted_list)); + memset (rid_all_sorted_list, 0, sizeof (rid_all_sorted_list)); + memset (rid_lo_sorted_list, 0, sizeof (rid_lo_sorted_list)); memset (&rid_user_assigned, 0, sizeof (rid_user_assigned)); - rid_all_sorted_list.cmp = router_id_cmp; - rid_lo_sorted_list.cmp = router_id_cmp; + rid_all_sorted_list->cmp = router_id_cmp; + rid_lo_sorted_list->cmp = router_id_cmp; rid_user_assigned.family = AF_INET; rid_user_assigned.prefixlen = 32; diff --git a/zebra/rt.h b/zebra/rt.h index 8bfe5a42..7faa127b 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -36,8 +36,6 @@ extern int kernel_address_delete_ipv4 (struct interface *, struct connected *); #ifdef HAVE_IPV6 extern int kernel_add_ipv6 (struct prefix *, struct rib *); extern int kernel_delete_ipv6 (struct prefix *, struct rib *); -extern int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table); #endif /* HAVE_IPV6 */ diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c deleted file mode 100644 index a5d588c7..00000000 --- a/zebra/rt_ioctl.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * kernel routing table update by ioctl(). - * Copyright (C) 1997, 98 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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. - * - * GNU Zebra 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 General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "prefix.h" -#include "log.h" -#include "if.h" - -#include "zebra/zserv.h" -#include "zebra/rib.h" -#include "zebra/debug.h" -#include "zebra/rt.h" - -/* Initialize of kernel interface. There is no kernel communication - support under ioctl(). So this is dummy stub function. */ -void -kernel_init (void) -{ - return; -} - -/* Dummy function of routing socket. */ -static void -kernel_read (int sock) -{ - return; -} - -#if 0 -/* Initialization prototype of struct sockaddr_in. */ -static struct sockaddr_in sin_proto = -{ -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sizeof (struct sockaddr_in), -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - AF_INET, 0, {0}, {0} -}; -#endif /* 0 */ - -/* Solaris has ortentry. */ -#ifdef HAVE_OLD_RTENTRY -#define rtentry ortentry -#endif /* HAVE_OLD_RTENTRY */ - -/* Interface to ioctl route message. */ -int -kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate, - int index, int flags) -{ - int ret; - int sock; - struct rtentry rtentry; - struct sockaddr_in sin_dest, sin_mask, sin_gate; - - memset (&rtentry, 0, sizeof (struct rtentry)); - - /* Make destination. */ - memset (&sin_dest, 0, sizeof (struct sockaddr_in)); - sin_dest.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_dest.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_dest.sin_addr = dest->prefix; - - /* Make gateway. */ - if (gate) - { - memset (&sin_gate, 0, sizeof (struct sockaddr_in)); - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = *gate; - } - - memset (&sin_mask, 0, sizeof (struct sockaddr_in)); - sin_mask.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - masklen2ip (dest->prefixlen, &sin_mask.sin_addr); - - /* Set destination address, mask and gateway.*/ - memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); - if (gate) - memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); -#ifndef SUNOS_5 - memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); -#endif /* SUNOS_5 */ - - /* Routing entry flag set. */ - if (dest->prefixlen == 32) - rtentry.rt_flags |= RTF_HOST; - - if (gate && gate->s_addr != INADDR_ANY) - rtentry.rt_flags |= RTF_GATEWAY; - - rtentry.rt_flags |= RTF_UP; - - /* Additional flags */ - rtentry.rt_flags |= flags; - - - /* For tagging route. */ - /* rtentry.rt_flags |= RTF_DYNAMIC; */ - - /* Open socket for ioctl. */ - sock = socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message by ioctl(). */ - ret = ioctl (sock, SIOCADDRT, &rtentry); - if (ret < 0) - { - switch (errno) - { - case EEXIST: - close (sock); - return ZEBRA_ERR_RTEXIST; - break; - case ENETUNREACH: - close (sock); - return ZEBRA_ERR_RTUNREACH; - break; - case EPERM: - close (sock); - return ZEBRA_ERR_EPERM; - break; - } - - close (sock); - zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); - return 1; - } - close (sock); - - return ret; -} - -/* Interface to ioctl route message. */ -static int -kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) -{ - int ret; - int sock; - struct rtentry rtentry; - struct sockaddr_in sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; - int nexthop_num = 0; - struct interface *ifp; - - memset (&rtentry, 0, sizeof (struct rtentry)); - - /* Make destination. */ - memset (&sin_dest, 0, sizeof (struct sockaddr_in)); - sin_dest.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_dest.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_dest.sin_addr = p->u.prefix4; - - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) - { - SET_FLAG (rtentry.rt_flags, RTF_REJECT); - - if (cmd == SIOCADDRT) - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - goto skip; - } - - memset (&sin_gate, 0, sizeof (struct sockaddr_in)); - - /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - if ((cmd == SIOCADDRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == SIOCDELRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || - nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = nexthop->rgate.ipv4; - rtentry.rt_flags |= RTF_GATEWAY; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME) - { - ifp = if_lookup_by_index (nexthop->rifindex); - if (ifp) - rtentry.rt_dev = ifp->name; - else - return -1; - } - } - else - { - if (nexthop->type == NEXTHOP_TYPE_IPV4 || - nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = nexthop->gate.ipv4; - rtentry.rt_flags |= RTF_GATEWAY; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) - { - ifp = if_lookup_by_index (nexthop->ifindex); - if (ifp) - rtentry.rt_dev = ifp->name; - else - return -1; - } - } - - if (cmd == SIOCADDRT) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - nexthop_num++; - break; - } - } - - /* If there is no useful nexthop then return. */ - if (nexthop_num == 0) - { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_route_multipath(): No useful nexthop."); - return 0; - } - - skip: - - memset (&sin_mask, 0, sizeof (struct sockaddr_in)); - sin_mask.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_mask.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - masklen2ip (p->prefixlen, &sin_mask.sin_addr); - - /* Set destination address, mask and gateway.*/ - memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); - - if (rtentry.rt_flags & RTF_GATEWAY) - memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); - -#ifndef SUNOS_5 - memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); -#endif /* SUNOS_5 */ - - /* Metric. It seems metric minus one value is installed... */ - rtentry.rt_metric = rib->metric; - - /* Routing entry flag set. */ - if (p->prefixlen == 32) - rtentry.rt_flags |= RTF_HOST; - - rtentry.rt_flags |= RTF_UP; - - /* Additional flags */ - /* rtentry.rt_flags |= flags; */ - - /* For tagging route. */ - /* rtentry.rt_flags |= RTF_DYNAMIC; */ - - /* Open socket for ioctl. */ - sock = socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message by ioctl(). */ - ret = ioctl (sock, cmd, &rtentry); - if (ret < 0) - { - switch (errno) - { - case EEXIST: - close (sock); - return ZEBRA_ERR_RTEXIST; - break; - case ENETUNREACH: - close (sock); - return ZEBRA_ERR_RTUNREACH; - break; - case EPERM: - close (sock); - return ZEBRA_ERR_EPERM; - break; - } - - close (sock); - zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); - return ret; - } - close (sock); - - return ret; -} - -int -kernel_add_ipv4 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET); -} - -int -kernel_delete_ipv4 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET); -} - -#ifdef HAVE_IPV6 - -/* Below is hack for GNU libc definition and Linux 2.1.X header. */ -#undef RTF_DEFAULT -#undef RTF_ADDRCONF - -#include - -#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 -/* struct in6_rtmsg will be declared in net/route.h. */ -#else -#include -#endif - -static int -kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate, - int index, int flags) -{ - int ret; - int sock; - struct in6_rtmsg rtm; - - memset (&rtm, 0, sizeof (struct in6_rtmsg)); - - rtm.rtmsg_flags |= RTF_UP; - rtm.rtmsg_metric = 1; - memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr)); - rtm.rtmsg_dst_len = dest->prefixlen; - - /* We need link local index. But this should be done caller... - if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) - { - index = if_index_address (&rtm.rtmsg_gateway); - rtm.rtmsg_ifindex = index; - } - else - rtm.rtmsg_ifindex = 0; - */ - - rtm.rtmsg_flags |= RTF_GATEWAY; - - /* For tagging route. */ - /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ - - memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr)); - - if (index) - rtm.rtmsg_ifindex = index; - else - rtm.rtmsg_ifindex = 0; - - rtm.rtmsg_metric = 1; - - sock = socket (AF_INET6, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message via ioctl. */ - ret = ioctl (sock, type, &rtm); - if (ret < 0) - { - zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete", - safe_strerror(errno)); - ret = errno; - close (sock); - return ret; - } - close (sock); - - return ret; -} - -static int -kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, - int family) -{ - int ret; - int sock; - struct in6_rtmsg rtm; - struct nexthop *nexthop; - int nexthop_num = 0; - - memset (&rtm, 0, sizeof (struct in6_rtmsg)); - - rtm.rtmsg_flags |= RTF_UP; - rtm.rtmsg_metric = rib->metric; - memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr)); - rtm.rtmsg_dst_len = p->prefixlen; - - /* We need link local index. But this should be done caller... - if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) - { - index = if_index_address (&rtm.rtmsg_gateway); - rtm.rtmsg_ifindex = index; - } - else - rtm.rtmsg_ifindex = 0; - */ - - rtm.rtmsg_flags |= RTF_GATEWAY; - - /* For tagging route. */ - /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ - - /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - if ((cmd == SIOCADDRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == SIOCDELRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6, - sizeof (struct in6_addr)); - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - rtm.rtmsg_ifindex = nexthop->rifindex; - else - rtm.rtmsg_ifindex = 0; - - } - else - { - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, - sizeof (struct in6_addr)); - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - rtm.rtmsg_ifindex = nexthop->ifindex; - else - rtm.rtmsg_ifindex = 0; - } - - if (cmd == SIOCADDRT) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - nexthop_num++; - break; - } - } - - /* If there is no useful nexthop then return. */ - if (nexthop_num == 0) - { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_route_multipath(): No useful nexthop."); - return 0; - } - - sock = socket (AF_INET6, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message via ioctl. */ - ret = ioctl (sock, cmd, &rtm); - if (ret < 0) - { - zlog_warn ("can't %s ipv6 route: %s\n", - cmd == SIOCADDRT ? "add" : "delete", - safe_strerror(errno)); - ret = errno; - close (sock); - return ret; - } - close (sock); - - return ret; -} - -int -kernel_add_ipv6 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6); -} - -int -kernel_delete_ipv6 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6); -} - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags); -} -#endif /* HAVE_IPV6 */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ab28ad27..d49a6e2b 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -43,7 +43,7 @@ #include "zebra/interface.h" #include "zebra/debug.h" -#define NL_PKT_BUF_SIZE 4096 +#include "rt_netlink.h" /* Socket interface to kernel */ struct nlsock @@ -68,20 +68,6 @@ static const struct message nlmsg_str[] = { {0, NULL} }; -static const char *nexthop_types_desc[] = -{ - "none", - "Directly connected", - "Interface route", - "IPv4 nexthop", - "IPv4 nexthop with ifindex", - "IPv4 nexthop with ifname", - "IPv6 nexthop", - "IPv6 nexthop with ifindex", - "IPv6 nexthop with ifname", - "Null0 nexthop", -}; - extern struct zebra_t zebrad; extern struct zebra_privs_t zserv_privs; @@ -115,6 +101,10 @@ set_ifindex(struct interface *ifp, unsigned int ifi_index) ifp->ifindex = ifi_index; } +#ifndef SO_RCVBUFFORCE +#define SO_RCVBUFFORCE (33) +#endif + static int netlink_recvbuf (struct nlsock *nl, uint32_t newsize) { @@ -131,8 +121,16 @@ netlink_recvbuf (struct nlsock *nl, uint32_t newsize) return -1; } - ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize, + /* Try force option (linux >= 2.6.14) and fall back to normal set */ + if ( zserv_privs.change (ZPRIVS_RAISE) ) + zlog_err ("routing_socket: Can't raise privileges"); + ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize, sizeof(nl_rcvbufsize)); + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("routing_socket: Can't lower privileges"); + if (ret < 0) + ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize, + sizeof(nl_rcvbufsize)); if (ret < 0) { zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name, @@ -164,6 +162,12 @@ netlink_socket (struct nlsock *nl, unsigned long groups) int namelen; int save_errno; + if (zserv_privs.change (ZPRIVS_RAISE)) + { + zlog (NULL, LOG_ERR, "Can't raise privileges"); + return -1; + } + sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { @@ -177,12 +181,6 @@ netlink_socket (struct nlsock *nl, unsigned long groups) snl.nl_groups = groups; /* Bind the socket to the netlink structure for anything. */ - if (zserv_privs.change (ZPRIVS_RAISE)) - { - zlog (NULL, LOG_ERR, "Can't raise privileges"); - return -1; - } - ret = bind (sock, (struct sockaddr *) &snl, sizeof snl); save_errno = errno; if (zserv_privs.change (ZPRIVS_LOWER)) @@ -284,9 +282,17 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), while (1) { char buf[NL_PKT_BUF_SIZE]; - struct iovec iov = { buf, sizeof buf }; + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof buf + }; struct sockaddr_nl snl; - struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + struct msghdr msg = { + .msg_name = (void *) &snl, + .msg_namelen = sizeof snl, + .msg_iov = &iov, + .msg_iovlen = 1 + }; struct nlmsghdr *h; status = recvmsg (nl->sock, &msg, 0); @@ -383,8 +389,11 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, h->nlmsg_seq, h->nlmsg_pid); - /* skip unsolicited messages originating from command socket */ - if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid) + /* skip unsolicited messages originating from command socket + * linux sets the originators port-id for {NEW|DEL}ADDR messages, + * so this has to be checked here. */ + if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid + && (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("netlink_parse_info: %s packet comes from %s", @@ -503,7 +512,7 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) set_ifindex(ifp, ifi->ifi_index); ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; /* Hardware type and address. */ ifp->hw_type = ifi->ifi_type; @@ -1083,7 +1092,7 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) set_ifindex(ifp, ifi->ifi_index); ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; netlink_interface_update_hw_addr (tb, ifp); @@ -1095,7 +1104,7 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Interface status change. */ set_ifindex(ifp, ifi->ifi_index); ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; netlink_interface_update_hw_addr (tb, ifp); @@ -1236,10 +1245,10 @@ netlink_route_read (void) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ -static int -addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) +int +addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, int alen) { - int len; + size_t len; struct rtattr *rta; len = RTA_LENGTH (alen); @@ -1256,7 +1265,7 @@ addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) return 0; } -static int +int rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) { int len; @@ -1278,10 +1287,10 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ -static int -addattr32 (struct nlmsghdr *n, int maxlen, int type, int data) +int +addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data) { - int len; + size_t len; struct rtattr *rta; len = RTA_LENGTH (4); @@ -1311,8 +1320,16 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl) { int status; struct sockaddr_nl snl; - struct iovec iov = { (void *) n, n->nlmsg_len }; - struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + struct iovec iov = { + .iov_base = (void *) n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = (void *) &snl, + .msg_namelen = sizeof snl, + .msg_iov = &iov, + .msg_iovlen = 1, + }; int save_errno; memset (&snl, 0, sizeof snl); @@ -1425,6 +1442,218 @@ netlink_route (int cmd, int family, void *dest, int length, void *gate, return 0; } +/* This function takes a nexthop as argument and adds + * the appropriate netlink attributes to an existing + * netlink message. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param nlmsg: nlmsghdr structure to fill in. + * @param req_size: The size allocated for the message. + */ +static void +_netlink_route_build_singlepath( + const char *routedesc, + int bytelen, + struct rib *rib, + struct nexthop *nexthop, + struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, + size_t req_size) +{ + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtmsg->rtm_flags |= RTNH_F_ONLINK; + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (rib->type == ZEBRA_ROUTE_OLSR) + rtmsg->rtm_scope = RT_SCOPE_LINK; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } +} + +/* This function takes a nexthop as argument and + * appends to the given rtattr/rtnexthop pair the + * representation of the nexthop. If the nexthop + * defines a preferred source, the src parameter + * will be modified to point to that src, otherwise + * it will be kept unmodified. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param rta: rtnetlink attribute structure + * @param rtnh: pointer to an rtnetlink nexthop structure + * @param src: pointer pointing to a location where + * the prefsrc should be stored. + */ +static void +_netlink_route_build_multipath( + const char *routedesc, + int bytelen, + struct nexthop *nexthop, + struct rtattr *rta, + struct rtnexthop *rtnh, + union g_addr **src + ) +{ + rtnh->rtnh_len = sizeof (*rtnh); + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rta->rta_len += rtnh->rtnh_len; + + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtnh->rtnh_flags |= RTNH_F_ONLINK; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + /* ifindex */ + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else + { + rtnh->rtnh_ifindex = 0; + } +} + +/* Log debug information for netlink_route_multipath + * if debug logging is enabled. + * + * @param cmd: Netlink command which is to be processed + * @param p: Prefix for which the change is due + * @param nexthop: Nexthop which is currently processed + * @param routedesc: Semantic annotation for nexthop + * (recursive, multipath, etc.) + * @param family: Address family which the change concerns + */ +static void +_netlink_route_debug( + int cmd, + struct prefix *p, + struct nexthop *nexthop, + const char *routedesc, + int family) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("netlink_route_multipath() (%s): %s %s/%d type %s", + routedesc, + lookup (nlmsg_str, cmd), +#ifdef HAVE_IPV6 + (family == AF_INET) ? inet_ntoa (p->u.prefix4) : + inet6_ntoa (p->u.prefix6), +#else + inet_ntoa (p->u.prefix4), +#endif /* HAVE_IPV6 */ + p->prefixlen, nexthop_type_to_str (nexthop->type)); + } +} + /* Routing table change via netlink interface. */ static int netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, @@ -1432,9 +1661,11 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, { int bytelen; struct sockaddr_nl snl; - struct nexthop *nexthop = NULL; - int nexthop_num = 0; + struct nexthop *nexthop = NULL, *tnexthop; + int recursing; + int nexthop_num; int discard; + const char *routedesc; struct { @@ -1484,159 +1715,53 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, if (discard) { if (cmd == RTM_NEWROUTE) - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + /* We shouldn't encounter recursive nexthops on discard routes, + * but it is probably better to handle that case correctly anyway. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } goto skip; } - /* Multipath case. */ - if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) + /* Count overall nexthops so we can decide whether to use singlepath + * or multipath case. */ + nexthop_num = 0; + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if (cmd == RTM_NEWROUTE && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + continue; + + nexthop_num++; + } + + /* Singlepath case. */ + if (nexthop_num == 1 || MULTIPATH_NUM == 1) + { + nexthop_num = 0; + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { + routedesc = recursing ? "recursive, 1 hop" : "single hop"; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug - ("netlink_route_multipath() (recursive, 1 hop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - - p->prefixlen, nexthop_types_desc[nexthop->rtype]); - } - - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->rgate.ipv4, bytelen); - if (nexthop->src.ipv4.s_addr) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via %s if %u", - inet_ntoa (nexthop->rgate.ipv4), - nexthop->rifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->rgate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via %s if %u", - inet6_ntoa (nexthop->rgate.ipv6), - nexthop->rifindex); - } -#endif /* HAVE_IPV6 */ - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr32 (&req.n, sizeof req, RTA_OIF, - nexthop->rifindex); - if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFINDEX) - && nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via if %u", - nexthop->rifindex); - } - } - else - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug - ("netlink_route_multipath() (single hop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->type]); - } - - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - if (nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via %s if %u", - inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via %s if %u", - inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); - } -#endif /* HAVE_IPV6 */ - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); - - if (nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via if %u", nexthop->ifindex); - } - else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via if %u", nexthop->ifindex); - } - } + _netlink_route_debug(cmd, p, nexthop, routedesc, family); + _netlink_route_build_singlepath(routedesc, bytelen, rib, + nexthop, &req.n, &req.r, + sizeof req); if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1658,168 +1783,26 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, rtnh = RTA_DATA (rta); nexthop_num = 0; - for (nexthop = rib->nexthop; - nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM); - nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { + routedesc = recursing ? "recursive, multihop" : "multihop"; nexthop_num++; - rtnh->rtnh_len = sizeof (*rtnh); - rtnh->rtnh_flags = 0; - rtnh->rtnh_hops = 0; - rta->rta_len += rtnh->rtnh_len; - - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug ("netlink_route_multipath() " - "(recursive, multihop): %s %s/%d type %s", - lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->rtype]); - } - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->rgate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + 4; - - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via %s if %u", - inet_ntoa (nexthop->rgate.ipv4), - nexthop->rifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->rgate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via %s if %u", - inet6_ntoa (nexthop->rgate.ipv6), - nexthop->rifindex); - } -#endif /* HAVE_IPV6 */ - /* ifindex */ - if (nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->rifindex; - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via if %u", - nexthop->rifindex); - } - else if (nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->rifindex; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via if %u", - nexthop->rifindex); - } - else - { - rtnh->rtnh_ifindex = 0; - } - } - else - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug ("netlink_route_multipath() (multihop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->type]); - } - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + 4; - - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via %s if %u", - inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via %s if %u", - inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); - } -#endif /* HAVE_IPV6 */ - /* ifindex */ - if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->ifindex; - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via if %u", nexthop->ifindex); - } - else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rtnh->rtnh_ifindex = nexthop->ifindex; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via if %u", nexthop->ifindex); - } - else - { - rtnh->rtnh_ifindex = 0; - } - } + _netlink_route_debug(cmd, p, nexthop, + routedesc, family); + _netlink_route_build_multipath(routedesc, bytelen, + nexthop, rta, rtnh, &src); rtnh = RTNH_NEXT (rtnh); if (cmd == RTM_NEWROUTE) @@ -1876,17 +1859,8 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib) { return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET6); } - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - return netlink_route (RTM_DELROUTE, AF_INET6, &dest->prefix, - dest->prefixlen, gate, index, flags, table); -} #endif /* HAVE_IPV6 */ - + /* Interface address modification. */ static int netlink_address (int cmd, int family, struct interface *ifp, @@ -2023,3 +1997,21 @@ kernel_init (void) thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); } } + +/* + * nl_msg_type_to_str + */ +const char * +nl_msg_type_to_str (uint16_t msg_type) +{ + return lookup (nlmsg_str, msg_type); +} + +/* + * nl_rtproto_to_str + */ +const char * +nl_rtproto_to_str (u_char rtproto) +{ + return lookup (rtproto_str, rtproto); +} diff --git a/zebra/ipforward_aix.c b/zebra/rt_netlink.h similarity index 53% rename from zebra/ipforward_aix.c rename to zebra/rt_netlink.h index c79e7f1c..0facd49e 100644 --- a/zebra/ipforward_aix.c +++ b/zebra/rt_netlink.h @@ -1,6 +1,5 @@ -/* - * ipforward value get function for aix. - * Copyright (C) 1997 Kunihiro Ishiguro +/* Header file exported by rt_netlink.c to zebra. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro * * This file is part of GNU Zebra. * @@ -17,48 +16,31 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ -#include +#ifndef _ZEBRA_RT_NETLINK_H +#define _ZEBRA_RT_NETLINK_H -int -ipforward () -{ - int fd, ret; - int af = AF_INET; - char netopt[] = "ipforwarding"; - struct optreq oq; +#ifdef HAVE_NETLINK - fd = socket(af, SOCK_DGRAM, 0); - if (fd < 0) { - /* need logging here */ - return -1; - } +#define NL_PKT_BUF_SIZE 8192 - strcpy (oq.name, netopt); - oq.getnext = 0; +extern int +addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data); +extern int +addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, int alen); - ret = ioctl (fd, SIOCGNETOPT, (caddr_t)&oq); - close(fd); +extern int +rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen); - if (ret < 0) { - /* need logging here */ - return -1; - } +extern const char * +nl_msg_type_to_str (uint16_t msg_type); - ret = atoi (oq.data); - return ret; -} +extern const char * +nl_rtproto_to_str (u_char rtproto); -int -ipforward_on () -{ - ; -} -int -ipforward_off () -{ - ; -} +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_RT_NETLINK_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 1b8ded7e..63470adc 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -71,7 +71,8 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in *mask = NULL; struct sockaddr_in sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; unsigned int ifindex = 0; int gate = 0; @@ -96,8 +97,11 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + gate = 0; char gate_buf[INET_ADDRSTRLEN] = "NULL"; @@ -112,38 +116,22 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) )) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + if (nexthop->type == NEXTHOP_TYPE_IPV4 || + nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || - nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_addr = nexthop->rgate.ipv4; - gate = 1; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - ifindex = nexthop->rifindex; + sin_gate.sin_addr = nexthop->gate.ipv4; + gate = 1; } - else + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + ifindex = nexthop->ifindex; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 || - nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_addr = nexthop->gate.ipv4; - gate = 1; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - ifindex = nexthop->ifindex; - if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) - { - struct in_addr loopback; - loopback.s_addr = htonl (INADDR_LOOPBACK); - sin_gate.sin_addr = loopback; - gate = 1; - } + struct in_addr loopback; + loopback.s_addr = htonl (INADDR_LOOPBACK); + sin_gate.sin_addr = loopback; + gate = 1; } if (gate && p->prefixlen == 32) @@ -172,7 +160,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { zlog_debug ("%s: %s/%d: attention! gate not found for rib %p", __func__, prefix_buf, p->prefixlen, rib); - rib_dump (__func__, (struct prefix_ipv4 *)p, rib); + rib_dump (p, rib); } else inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN); @@ -219,7 +207,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: odd command %s for flags %d", __func__, lookup (rtm_type_str, cmd), nexthop->flags); - } /* for (nexthop = ... */ + } /* for (ALL_NEXTHOPS_RO(...))*/ /* If there was no useful nexthop, then complain. */ if (nexthop_num == 0 && IS_ZEBRA_DEBUG_KERNEL) @@ -266,13 +254,8 @@ sin6_masklen (struct in6_addr mask) char *p, *lim; int len; -#if defined (INRIA) - if (IN_ANYADDR6 (mask)) - return sizeof (long); -#else /* ! INRIA */ if (IN6_IS_ADDR_UNSPECIFIED (&mask)) return sizeof (long); -#endif /* ! INRIA */ sin6.sin6_addr = mask; len = sizeof (struct sockaddr_in6); @@ -354,7 +337,8 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, { struct sockaddr_in6 *mask; struct sockaddr_in6 sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; unsigned int ifindex = 0; int gate = 0; @@ -376,8 +360,11 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + gate = 0; if ((cmd == RTM_ADD @@ -388,36 +375,18 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, #endif )) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - sin_gate.sin6_addr = nexthop->rgate.ipv6; - gate = 1; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - ifindex = nexthop->rifindex; - } - else + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - sin_gate.sin6_addr = nexthop->gate.ipv6; - gate = 1; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - ifindex = nexthop->ifindex; + sin_gate.sin6_addr = nexthop->gate.ipv6; + gate = 1; } + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + ifindex = nexthop->ifindex; if (cmd == RTM_ADD) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); @@ -505,20 +474,4 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib) return route; } - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - int route; - - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv6 (RTM_DELETE, dest, gate, index, flags); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); - - return route; -} #endif /* HAVE_IPV6 */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index ae5c5a1c..21ca6da9 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -66,7 +66,7 @@ static void rtadv_event (enum rtadv_event, int); static int if_join_all_router (int, struct interface *); static int if_leave_all_router (int, struct interface *); - + /* Structure which hold status of router advertisement. */ struct rtadv { @@ -80,7 +80,7 @@ struct rtadv }; struct rtadv *rtadv = NULL; - + static struct rtadv * rtadv_new (void) { @@ -580,7 +580,7 @@ rtadv_make_socket (void) return sock; } - + static struct rtadv_prefix * rtadv_prefix_new (void) { @@ -1483,7 +1483,7 @@ DEFUN (no_ipv6_nd_router_preference, ALIAS (no_ipv6_nd_router_preference, no_ipv6_nd_router_preference_val_cmd, - "no ipv6 nd router-preference (high|medium|low", + "no ipv6 nd router-preference (high|medium|low)", NO_STR "Interface IPv6 config commands\n" "Neighbor discovery\n" diff --git a/zebra/rtread_proc.c b/zebra/rtread_proc.c deleted file mode 100644 index 07e8491a..00000000 --- a/zebra/rtread_proc.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Kernel routing readup by /proc filesystem - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra 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. - * - * GNU Zebra 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 General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "prefix.h" -#include "log.h" -#include "if.h" -#include "rib.h" - -#include "zebra/zserv.h" -#include "zebra/rt.h" - -/* Proc file system to read IPv4 routing table. */ -#ifndef _PATH_PROCNET_ROUTE -#define _PATH_PROCNET_ROUTE "/proc/net/route" -#endif /* _PATH_PROCNET_ROUTE */ - -/* Proc file system to read IPv6 routing table. */ -#ifndef _PATH_PROCNET_ROUTE6 -#define _PATH_PROCNET_ROUTE6 "/proc/net/ipv6_route" -#endif /* _PATH_PROCNET_ROUTE6 */ - -/* To read interface's name */ -#define INTERFACE_NAMSIZ 20 - -/* Reading buffer for one routing entry. */ -#define RT_BUFSIZ 1024 - -/* Kernel routing table read up by /proc filesystem. */ -static int -proc_route_read (void) -{ - FILE *fp; - char buf[RT_BUFSIZ]; - char iface[INTERFACE_NAMSIZ], dest[9], gate[9], mask[9]; - int flags, refcnt, use, metric, mtu, window, rtt; - - /* Open /proc filesystem */ - fp = fopen (_PATH_PROCNET_ROUTE, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open %s : %s\n", _PATH_PROCNET_ROUTE, safe_strerror (errno)); - return -1; - } - - /* Drop first label line. */ - fgets (buf, RT_BUFSIZ, fp); - - while (fgets (buf, RT_BUFSIZ, fp) != NULL) - { - int n; - struct prefix_ipv4 p; - struct in_addr tmpmask; - struct in_addr gateway; - u_char zebra_flags = 0; - - n = sscanf (buf, "%s %s %s %x %d %d %d %s %d %d %d", - iface, dest, gate, &flags, &refcnt, &use, &metric, - mask, &mtu, &window, &rtt); - if (n != 11) - { - zlog_warn ("can't read all of routing information\n"); - continue; - } - if (! (flags & RTF_UP)) - continue; - if (! (flags & RTF_GATEWAY)) - continue; - - if (flags & RTF_DYNAMIC) - zebra_flags |= ZEBRA_FLAG_SELFROUTE; - - p.family = AF_INET; - sscanf (dest, "%lX", (unsigned long *)&p.prefix); - sscanf (mask, "%lX", (unsigned long *)&tmpmask); - p.prefixlen = ip_masklen (tmpmask); - sscanf (gate, "%lX", (unsigned long *)&gateway); - - rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gateway, NULL, 0, 0, 0, 0, SAFI_UNICAST); - } - - fclose (fp); - return 0; -} - -#ifdef HAVE_IPV6 -static int -proc_ipv6_route_read () -{ - FILE *fp; - char buf [RT_BUFSIZ]; - - /* Open /proc filesystem */ - fp = fopen (_PATH_PROCNET_ROUTE6, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open %s : %s", _PATH_PROCNET_ROUTE6, - safe_strerror (errno)); - return -1; - } - - /* There is no title line, so we don't drop first line. */ - while (fgets (buf, RT_BUFSIZ, fp) != NULL) - { - int n; - char dest[33], src[33], gate[33]; - char iface[INTERFACE_NAMSIZ]; - int dest_plen, src_plen; - int metric, use, refcnt, flags; - struct prefix_ipv6 p; - struct in6_addr gateway; - u_char zebra_flags = 0; - - /* Linux 2.1.x write this information at net/ipv6/route.c - rt6_info_node () */ - n = sscanf (buf, "%32s %02x %32s %02x %32s %08x %08x %08x %08x %s", - dest, &dest_plen, src, &src_plen, gate, - &metric, &use, &refcnt, &flags, iface); - - if (n != 10) - { - /* zlog_warn ("can't read all of routing information %d\n%s\n", n, buf); */ - continue; - } - - if (! (flags & RTF_UP)) - continue; - if (! (flags & RTF_GATEWAY)) - continue; - - if (flags & RTF_DYNAMIC) - zebra_flags |= ZEBRA_FLAG_SELFROUTE; - - p.family = AF_INET6; - str2in6_addr (dest, &p.prefix); - str2in6_addr (gate, &gateway); - p.prefixlen = dest_plen; - - rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gateway, 0, 0, - metric, 0); - } - - fclose (fp); - return 0; -} -#endif /* HAVE_IPV6 */ - -void -route_read (void) -{ - proc_route_read (); -#ifdef HAVE_IPV6 - proc_ipv6_route_read (); -#endif /* HAVE_IPV6 */ -} diff --git a/zebra/test_main.c b/zebra/test_main.c index a9518637..f98bb419 100644 --- a/zebra/test_main.c +++ b/zebra/test_main.c @@ -103,7 +103,7 @@ usage (char *progname, int status) exit (status); } - + static unsigned int test_ifindex = 0; /* testrib commands */ @@ -149,7 +149,7 @@ test_cmd_init (void) { install_element (INTERFACE_NODE, &test_interface_state_cmd); } - + /* SIGHUP handler. */ static void sighup (void) @@ -195,7 +195,7 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + /* Main startup routine. */ int main (int argc, char **argv) @@ -298,9 +298,6 @@ main (int argc, char **argv) route_read (); zebra_vty_init(); - /* Sort VTY commands. */ - sort_node (); - /* Configuration file read*/ vty_read_config (config_file, config_default); diff --git a/zebra/testrib.conf b/zebra/testrib.conf index 75ba0ccd..0df7dc24 100644 --- a/zebra/testrib.conf +++ b/zebra/testrib.conf @@ -11,45 +11,65 @@ debug zebra kernel ! interface eth0 ip address 10.0.0.1/24 + ipv6 address 1::0:1/64 state up ! interface eth1 ip address 10.0.1.1/24 + ipv6 address 1::1:1/64 ! interface eth2 ip address 10.0.2.1/24 + ipv6 address 1::2:1/64 ! +! Unnumbered interface foo1 ip address 192.168.1.1/32 + ipv6 address 2::1:1/128 ! interface foo0 ip address 192.168.1.1/32 ip address 192.168.1.1/24 label foo + ipv6 address 2::1:1/128 state up ! -ip route 1.1.1.0/24 1.1.2.2 ! statics that should be subsumed by connected routes, according to interface ! state ip route 10.0.0.0/24 10.0.1.254 ip route 10.0.1.0/24 10.0.2.254 ip route 10.0.2.0/24 10.0.0.254 +ipv6 route 1::0:0/64 1::1:f +ipv6 route 1::1:0/64 1::2:f +ipv6 route 1::2:0/64 1::0:f + +! null route +ip route 10.1.0.1/32 null0 +ipv6 route 100::1:1/128 null0 -! normalish route +! normalish routes ip route 1.1.2.0/24 10.0.0.2 +ipv6 route 80::/64 1::0:e + ! different admin distances ip route 1.1.0.2/32 10.0.0.3 10 ip route 1.1.0.2/32 10.0.0.4 20 ip route 1.1.0.2/32 10.0.1.3 30 +ipv6 route 90::1/128 1::0:a 10 +ipv6 route 90::1/128 1::0:b 20 +ipv6 route 90::1/128 1::1:c 30 + ! multiple-nexthop + distance ip route 1.1.0.2/32 10.0.0.5 10 +ipv6 route 90::1/128 1::0:d 10 ! a recursive route, potentially. ip route 1.1.3.0/24 10.0.0.2 ! double recursive, potentially ip route 1.1.0.1/32 1.1.3.1 ! +ip route 1.1.1.0/24 1.1.2.2 line vty exec-timeout 0 0 diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c new file mode 100644 index 00000000..e02d1745 --- /dev/null +++ b/zebra/zebra_fpm.c @@ -0,0 +1,1581 @@ +/* + * Main implementation file for interface to Forwarding Plane Manager. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "log.h" +#include "stream.h" +#include "thread.h" +#include "network.h" +#include "command.h" + +#include "zebra/rib.h" + +#include "fpm/fpm.h" +#include "zebra_fpm.h" +#include "zebra_fpm_private.h" + +/* + * Interval at which we attempt to connect to the FPM. + */ +#define ZFPM_CONNECT_RETRY_IVL 5 + +/* + * Sizes of outgoing and incoming stream buffers for writing/reading + * FPM messages. + */ +#define ZFPM_OBUF_SIZE (2 * FPM_MAX_MSG_LEN) +#define ZFPM_IBUF_SIZE (FPM_MAX_MSG_LEN) + +/* + * The maximum number of times the FPM socket write callback can call + * 'write' before it yields. + */ +#define ZFPM_MAX_WRITES_PER_RUN 10 + +/* + * Interval over which we collect statistics. + */ +#define ZFPM_STATS_IVL_SECS 10 + +/* + * Structure that holds state for iterating over all route_node + * structures that are candidates for being communicated to the FPM. + */ +typedef struct zfpm_rnodes_iter_t_ +{ + rib_tables_iter_t tables_iter; + route_table_iter_t iter; +} zfpm_rnodes_iter_t; + +/* + * Statistics. + */ +typedef struct zfpm_stats_t_ { + unsigned long connect_calls; + unsigned long connect_no_sock; + + unsigned long read_cb_calls; + + unsigned long write_cb_calls; + unsigned long write_calls; + unsigned long partial_writes; + unsigned long max_writes_hit; + unsigned long t_write_yields; + + unsigned long nop_deletes_skipped; + unsigned long route_adds; + unsigned long route_dels; + + unsigned long updates_triggered; + unsigned long redundant_triggers; + unsigned long non_fpm_table_triggers; + + unsigned long dests_del_after_update; + + unsigned long t_conn_down_starts; + unsigned long t_conn_down_dests_processed; + unsigned long t_conn_down_yields; + unsigned long t_conn_down_finishes; + + unsigned long t_conn_up_starts; + unsigned long t_conn_up_dests_processed; + unsigned long t_conn_up_yields; + unsigned long t_conn_up_aborts; + unsigned long t_conn_up_finishes; + +} zfpm_stats_t; + +/* + * States for the FPM state machine. + */ +typedef enum { + + /* + * In this state we are not yet ready to connect to the FPM. This + * can happen when this module is disabled, or if we're cleaning up + * after a connection has gone down. + */ + ZFPM_STATE_IDLE, + + /* + * Ready to talk to the FPM and periodically trying to connect to + * it. + */ + ZFPM_STATE_ACTIVE, + + /* + * In the middle of bringing up a TCP connection. Specifically, + * waiting for a connect() call to complete asynchronously. + */ + ZFPM_STATE_CONNECTING, + + /* + * TCP connection to the FPM is up. + */ + ZFPM_STATE_ESTABLISHED + +} zfpm_state_t; + +/* + * Globals. + */ +typedef struct zfpm_glob_t_ +{ + + /* + * True if the FPM module has been enabled. + */ + int enabled; + + struct thread_master *master; + + zfpm_state_t state; + + /* + * Port on which the FPM is running. + */ + int fpm_port; + + /* + * List of rib_dest_t structures to be processed + */ + TAILQ_HEAD (zfpm_dest_q, rib_dest_t_) dest_q; + + /* + * Stream socket to the FPM. + */ + int sock; + + /* + * Buffers for messages to/from the FPM. + */ + struct stream *obuf; + struct stream *ibuf; + + /* + * Threads for I/O. + */ + struct thread *t_connect; + struct thread *t_write; + struct thread *t_read; + + /* + * Thread to clean up after the TCP connection to the FPM goes down + * and the state that belongs to it. + */ + struct thread *t_conn_down; + + struct { + zfpm_rnodes_iter_t iter; + } t_conn_down_state; + + /* + * Thread to take actions once the TCP conn to the FPM comes up, and + * the state that belongs to it. + */ + struct thread *t_conn_up; + + struct { + zfpm_rnodes_iter_t iter; + } t_conn_up_state; + + unsigned long connect_calls; + time_t last_connect_call_time; + + /* + * Stats from the start of the current statistics interval up to + * now. These are the counters we typically update in the code. + */ + zfpm_stats_t stats; + + /* + * Statistics that were gathered in the last collection interval. + */ + zfpm_stats_t last_ivl_stats; + + /* + * Cumulative stats from the last clear to the start of the current + * statistics interval. + */ + zfpm_stats_t cumulative_stats; + + /* + * Stats interval timer. + */ + struct thread *t_stats; + + /* + * If non-zero, the last time when statistics were cleared. + */ + time_t last_stats_clear_time; + +} zfpm_glob_t; + +static zfpm_glob_t zfpm_glob_space; +static zfpm_glob_t *zfpm_g = &zfpm_glob_space; + +static int zfpm_read_cb (struct thread *thread); +static int zfpm_write_cb (struct thread *thread); + +static void zfpm_set_state (zfpm_state_t state, const char *reason); +static void zfpm_start_connect_timer (const char *reason); +static void zfpm_start_stats_timer (void); + +/* + * zfpm_thread_should_yield + */ +static inline int +zfpm_thread_should_yield (struct thread *t) +{ + return thread_should_yield (t); +} + +/* + * zfpm_state_to_str + */ +static const char * +zfpm_state_to_str (zfpm_state_t state) +{ + switch (state) + { + + case ZFPM_STATE_IDLE: + return "idle"; + + case ZFPM_STATE_ACTIVE: + return "active"; + + case ZFPM_STATE_CONNECTING: + return "connecting"; + + case ZFPM_STATE_ESTABLISHED: + return "established"; + + default: + return "unknown"; + } +} + +/* + * zfpm_get_time + */ +static time_t +zfpm_get_time (void) +{ + struct timeval tv; + + if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) + zlog_warn ("FPM: quagga_gettime failed!!"); + + return tv.tv_sec; +} + +/* + * zfpm_get_elapsed_time + * + * Returns the time elapsed (in seconds) since the given time. + */ +static time_t +zfpm_get_elapsed_time (time_t reference) +{ + time_t now; + + now = zfpm_get_time (); + + if (now < reference) + { + assert (0); + return 0; + } + + return now - reference; +} + +/* + * zfpm_is_table_for_fpm + * + * Returns TRUE if the the given table is to be communicated to the + * FPM. + */ +static inline int +zfpm_is_table_for_fpm (struct route_table *table) +{ + rib_table_info_t *info; + + info = rib_table_info (table); + + /* + * We only send the unicast tables in the main instance to the FPM + * at this point. + */ + if (info->vrf->id != 0) + return 0; + + if (info->safi != SAFI_UNICAST) + return 0; + + return 1; +} + +/* + * zfpm_rnodes_iter_init + */ +static inline void +zfpm_rnodes_iter_init (zfpm_rnodes_iter_t *iter) +{ + memset (iter, 0, sizeof (*iter)); + rib_tables_iter_init (&iter->tables_iter); + + /* + * This is a hack, but it makes implementing 'next' easier by + * ensuring that route_table_iter_next() will return NULL the first + * time we call it. + */ + route_table_iter_init (&iter->iter, NULL); + route_table_iter_cleanup (&iter->iter); +} + +/* + * zfpm_rnodes_iter_next + */ +static inline struct route_node * +zfpm_rnodes_iter_next (zfpm_rnodes_iter_t *iter) +{ + struct route_node *rn; + struct route_table *table; + + while (1) + { + rn = route_table_iter_next (&iter->iter); + if (rn) + return rn; + + /* + * We've made our way through this table, go to the next one. + */ + route_table_iter_cleanup (&iter->iter); + + while ((table = rib_tables_iter_next (&iter->tables_iter))) + { + if (zfpm_is_table_for_fpm (table)) + break; + } + + if (!table) + return NULL; + + route_table_iter_init (&iter->iter, table); + } + + return NULL; +} + +/* + * zfpm_rnodes_iter_pause + */ +static inline void +zfpm_rnodes_iter_pause (zfpm_rnodes_iter_t *iter) +{ + route_table_iter_pause (&iter->iter); +} + +/* + * zfpm_rnodes_iter_cleanup + */ +static inline void +zfpm_rnodes_iter_cleanup (zfpm_rnodes_iter_t *iter) +{ + route_table_iter_cleanup (&iter->iter); + rib_tables_iter_cleanup (&iter->tables_iter); +} + +/* + * zfpm_stats_init + * + * Initialize a statistics block. + */ +static inline void +zfpm_stats_init (zfpm_stats_t *stats) +{ + memset (stats, 0, sizeof (*stats)); +} + +/* + * zfpm_stats_reset + */ +static inline void +zfpm_stats_reset (zfpm_stats_t *stats) +{ + zfpm_stats_init (stats); +} + +/* + * zfpm_stats_copy + */ +static inline void +zfpm_stats_copy (const zfpm_stats_t *src, zfpm_stats_t *dest) +{ + memcpy (dest, src, sizeof (*dest)); +} + +/* + * zfpm_stats_compose + * + * Total up the statistics in two stats structures ('s1 and 's2') and + * return the result in the third argument, 'result'. Note that the + * pointer 'result' may be the same as 's1' or 's2'. + * + * For simplicity, the implementation below assumes that the stats + * structure is composed entirely of counters. This can easily be + * changed when necessary. + */ +static void +zfpm_stats_compose (const zfpm_stats_t *s1, const zfpm_stats_t *s2, + zfpm_stats_t *result) +{ + const unsigned long *p1, *p2; + unsigned long *result_p; + int i, num_counters; + + p1 = (const unsigned long *) s1; + p2 = (const unsigned long *) s2; + result_p = (unsigned long *) result; + + num_counters = (sizeof (zfpm_stats_t) / sizeof (unsigned long)); + + for (i = 0; i < num_counters; i++) + { + result_p[i] = p1[i] + p2[i]; + } +} + +/* + * zfpm_read_on + */ +static inline void +zfpm_read_on (void) +{ + assert (!zfpm_g->t_read); + assert (zfpm_g->sock >= 0); + + THREAD_READ_ON (zfpm_g->master, zfpm_g->t_read, zfpm_read_cb, 0, + zfpm_g->sock); +} + +/* + * zfpm_write_on + */ +static inline void +zfpm_write_on (void) +{ + assert (!zfpm_g->t_write); + assert (zfpm_g->sock >= 0); + + THREAD_WRITE_ON (zfpm_g->master, zfpm_g->t_write, zfpm_write_cb, 0, + zfpm_g->sock); +} + +/* + * zfpm_read_off + */ +static inline void +zfpm_read_off (void) +{ + THREAD_READ_OFF (zfpm_g->t_read); +} + +/* + * zfpm_write_off + */ +static inline void +zfpm_write_off (void) +{ + THREAD_WRITE_OFF (zfpm_g->t_write); +} + +/* + * zfpm_conn_up_thread_cb + * + * Callback for actions to be taken when the connection to the FPM + * comes up. + */ +static int +zfpm_conn_up_thread_cb (struct thread *thread) +{ + struct route_node *rnode; + zfpm_rnodes_iter_t *iter; + rib_dest_t *dest; + + assert (zfpm_g->t_conn_up); + zfpm_g->t_conn_up = NULL; + + iter = &zfpm_g->t_conn_up_state.iter; + + if (zfpm_g->state != ZFPM_STATE_ESTABLISHED) + { + zfpm_debug ("Connection not up anymore, conn_up thread aborting"); + zfpm_g->stats.t_conn_up_aborts++; + goto done; + } + + while ((rnode = zfpm_rnodes_iter_next (iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (dest) + { + zfpm_g->stats.t_conn_up_dests_processed++; + zfpm_trigger_update (rnode, NULL); + } + + /* + * Yield if need be. + */ + if (!zfpm_thread_should_yield (thread)) + continue; + + zfpm_g->stats.t_conn_up_yields++; + zfpm_rnodes_iter_pause (iter); + zfpm_g->t_conn_up = thread_add_background (zfpm_g->master, + zfpm_conn_up_thread_cb, + 0, 0); + return 0; + } + + zfpm_g->stats.t_conn_up_finishes++; + + done: + zfpm_rnodes_iter_cleanup (iter); + return 0; +} + +/* + * zfpm_connection_up + * + * Called when the connection to the FPM comes up. + */ +static void +zfpm_connection_up (const char *detail) +{ + assert (zfpm_g->sock >= 0); + zfpm_read_on (); + zfpm_write_on (); + zfpm_set_state (ZFPM_STATE_ESTABLISHED, detail); + + /* + * Start thread to push existing routes to the FPM. + */ + assert (!zfpm_g->t_conn_up); + + zfpm_rnodes_iter_init (&zfpm_g->t_conn_up_state.iter); + + zfpm_debug ("Starting conn_up thread"); + zfpm_g->t_conn_up = thread_add_background (zfpm_g->master, + zfpm_conn_up_thread_cb, 0, 0); + zfpm_g->stats.t_conn_up_starts++; +} + +/* + * zfpm_connect_check + * + * Check if an asynchronous connect() to the FPM is complete. + */ +static void +zfpm_connect_check () +{ + int status; + socklen_t slen; + int ret; + + zfpm_read_off (); + zfpm_write_off (); + + slen = sizeof (status); + ret = getsockopt (zfpm_g->sock, SOL_SOCKET, SO_ERROR, (void *) &status, + &slen); + + if (ret >= 0 && status == 0) + { + zfpm_connection_up ("async connect complete"); + return; + } + + /* + * getsockopt() failed or indicated an error on the socket. + */ + close (zfpm_g->sock); + zfpm_g->sock = -1; + + zfpm_start_connect_timer ("getsockopt() after async connect failed"); + return; +} + +/* + * zfpm_conn_down_thread_cb + * + * Callback that is invoked to clean up state after the TCP connection + * to the FPM goes down. + */ +static int +zfpm_conn_down_thread_cb (struct thread *thread) +{ + struct route_node *rnode; + zfpm_rnodes_iter_t *iter; + rib_dest_t *dest; + + assert (zfpm_g->state == ZFPM_STATE_IDLE); + + assert (zfpm_g->t_conn_down); + zfpm_g->t_conn_down = NULL; + + iter = &zfpm_g->t_conn_down_state.iter; + + while ((rnode = zfpm_rnodes_iter_next (iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (dest) + { + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) + { + TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries); + } + + UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + + zfpm_g->stats.t_conn_down_dests_processed++; + + /* + * Check if the dest should be deleted. + */ + rib_gc_dest(rnode); + } + + /* + * Yield if need be. + */ + if (!zfpm_thread_should_yield (thread)) + continue; + + zfpm_g->stats.t_conn_down_yields++; + zfpm_rnodes_iter_pause (iter); + zfpm_g->t_conn_down = thread_add_background (zfpm_g->master, + zfpm_conn_down_thread_cb, + 0, 0); + return 0; + } + + zfpm_g->stats.t_conn_down_finishes++; + zfpm_rnodes_iter_cleanup (iter); + + /* + * Start the process of connecting to the FPM again. + */ + zfpm_start_connect_timer ("cleanup complete"); + return 0; +} + +/* + * zfpm_connection_down + * + * Called when the connection to the FPM has gone down. + */ +static void +zfpm_connection_down (const char *detail) +{ + if (!detail) + detail = "unknown"; + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + + zlog_info ("connection to the FPM has gone down: %s", detail); + + zfpm_read_off (); + zfpm_write_off (); + + stream_reset (zfpm_g->ibuf); + stream_reset (zfpm_g->obuf); + + if (zfpm_g->sock >= 0) { + close (zfpm_g->sock); + zfpm_g->sock = -1; + } + + /* + * Start thread to clean up state after the connection goes down. + */ + assert (!zfpm_g->t_conn_down); + zfpm_debug ("Starting conn_down thread"); + zfpm_rnodes_iter_init (&zfpm_g->t_conn_down_state.iter); + zfpm_g->t_conn_down = thread_add_background (zfpm_g->master, + zfpm_conn_down_thread_cb, 0, 0); + zfpm_g->stats.t_conn_down_starts++; + + zfpm_set_state (ZFPM_STATE_IDLE, detail); +} + +/* + * zfpm_read_cb + */ +static int +zfpm_read_cb (struct thread *thread) +{ + size_t already; + struct stream *ibuf; + uint16_t msg_len; + fpm_msg_hdr_t *hdr; + + zfpm_g->stats.read_cb_calls++; + assert (zfpm_g->t_read); + zfpm_g->t_read = NULL; + + /* + * Check if async connect is now done. + */ + if (zfpm_g->state == ZFPM_STATE_CONNECTING) + { + zfpm_connect_check(); + return 0; + } + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + assert (zfpm_g->sock >= 0); + + ibuf = zfpm_g->ibuf; + + already = stream_get_endp (ibuf); + if (already < FPM_MSG_HDR_LEN) + { + ssize_t nbyte; + + nbyte = stream_read_try (ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already); + if (nbyte == 0 || nbyte == -1) + { + zfpm_connection_down ("closed socket in read"); + return 0; + } + + if (nbyte != (ssize_t) (FPM_MSG_HDR_LEN - already)) + goto done; + + already = FPM_MSG_HDR_LEN; + } + + stream_set_getp (ibuf, 0); + + hdr = (fpm_msg_hdr_t *) stream_pnt (ibuf); + + if (!fpm_msg_hdr_ok (hdr)) + { + zfpm_connection_down ("invalid message header"); + return 0; + } + + msg_len = fpm_msg_len (hdr); + + /* + * Read out the rest of the packet. + */ + if (already < msg_len) + { + ssize_t nbyte; + + nbyte = stream_read_try (ibuf, zfpm_g->sock, msg_len - already); + + if (nbyte == 0 || nbyte == -1) + { + zfpm_connection_down ("failed to read message"); + return 0; + } + + if (nbyte != (ssize_t) (msg_len - already)) + goto done; + } + + zfpm_debug ("Read out a full fpm message"); + + /* + * Just throw it away for now. + */ + stream_reset (ibuf); + + done: + zfpm_read_on (); + return 0; +} + +/* + * zfpm_writes_pending + * + * Returns TRUE if we may have something to write to the FPM. + */ +static int +zfpm_writes_pending (void) +{ + + /* + * Check if there is any data in the outbound buffer that has not + * been written to the socket yet. + */ + if (stream_get_endp (zfpm_g->obuf) - stream_get_getp (zfpm_g->obuf)) + return 1; + + /* + * Check if there are any prefixes on the outbound queue. + */ + if (!TAILQ_EMPTY (&zfpm_g->dest_q)) + return 1; + + return 0; +} + +/* + * zfpm_encode_route + * + * Encode a message to the FPM with information about the given route. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +static inline int +zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf, + size_t in_buf_len) +{ +#ifndef HAVE_NETLINK + return 0; +#else + + int cmd; + + cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE; + + return zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len); + +#endif /* HAVE_NETLINK */ +} + +/* + * zfpm_route_for_update + * + * Returns the rib that is to be sent to the FPM for a given dest. + */ +static struct rib * +zfpm_route_for_update (rib_dest_t *dest) +{ + struct rib *rib; + + RIB_DEST_FOREACH_ROUTE (dest, rib) + { + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + continue; + + return rib; + } + + /* + * We have no route for this destination. + */ + return NULL; +} + +/* + * zfpm_build_updates + * + * Process the outgoing queue and write messages to the outbound + * buffer. + */ +static void +zfpm_build_updates (void) +{ + struct stream *s; + rib_dest_t *dest; + unsigned char *buf, *data, *buf_end; + size_t msg_len; + size_t data_len; + fpm_msg_hdr_t *hdr; + struct rib *rib; + int is_add, write_msg; + + s = zfpm_g->obuf; + + assert (stream_empty (s)); + + do { + + /* + * Make sure there is enough space to write another message. + */ + if (STREAM_WRITEABLE (s) < FPM_MAX_MSG_LEN) + break; + + buf = STREAM_DATA (s) + stream_get_endp (s); + buf_end = buf + STREAM_WRITEABLE (s); + + dest = TAILQ_FIRST (&zfpm_g->dest_q); + if (!dest) + break; + + assert (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)); + + hdr = (fpm_msg_hdr_t *) buf; + hdr->version = FPM_PROTO_VERSION; + hdr->msg_type = FPM_MSG_TYPE_NETLINK; + + data = fpm_msg_data (hdr); + + rib = zfpm_route_for_update (dest); + is_add = rib ? 1 : 0; + + write_msg = 1; + + /* + * If this is a route deletion, and we have not sent the route to + * the FPM previously, skip it. + */ + if (!is_add && !CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM)) + { + write_msg = 0; + zfpm_g->stats.nop_deletes_skipped++; + } + + if (write_msg) { + data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data); + + assert (data_len); + if (data_len) + { + msg_len = fpm_data_len_to_msg_len (data_len); + hdr->msg_len = htons (msg_len); + stream_forward_endp (s, msg_len); + + if (is_add) + zfpm_g->stats.route_adds++; + else + zfpm_g->stats.route_dels++; + } + } + + /* + * Remove the dest from the queue, and reset the flag. + */ + UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries); + + if (is_add) + { + SET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + } + else + { + UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + } + + /* + * Delete the destination if necessary. + */ + if (rib_gc_dest (dest->rnode)) + zfpm_g->stats.dests_del_after_update++; + + } while (1); + +} + +/* + * zfpm_write_cb + */ +static int +zfpm_write_cb (struct thread *thread) +{ + struct stream *s; + int num_writes; + + zfpm_g->stats.write_cb_calls++; + assert (zfpm_g->t_write); + zfpm_g->t_write = NULL; + + /* + * Check if async connect is now done. + */ + if (zfpm_g->state == ZFPM_STATE_CONNECTING) + { + zfpm_connect_check (); + return 0; + } + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + assert (zfpm_g->sock >= 0); + + num_writes = 0; + + do + { + int bytes_to_write, bytes_written; + + s = zfpm_g->obuf; + + /* + * If the stream is empty, try fill it up with data. + */ + if (stream_empty (s)) + { + zfpm_build_updates (); + } + + bytes_to_write = stream_get_endp (s) - stream_get_getp (s); + if (!bytes_to_write) + break; + + bytes_written = write (zfpm_g->sock, STREAM_PNT (s), bytes_to_write); + zfpm_g->stats.write_calls++; + num_writes++; + + if (bytes_written < 0) + { + if (ERRNO_IO_RETRY (errno)) + break; + + zfpm_connection_down ("failed to write to socket"); + return 0; + } + + if (bytes_written != bytes_to_write) + { + + /* + * Partial write. + */ + stream_forward_getp (s, bytes_written); + zfpm_g->stats.partial_writes++; + break; + } + + /* + * We've written out the entire contents of the stream. + */ + stream_reset (s); + + if (num_writes >= ZFPM_MAX_WRITES_PER_RUN) + { + zfpm_g->stats.max_writes_hit++; + break; + } + + if (zfpm_thread_should_yield (thread)) + { + zfpm_g->stats.t_write_yields++; + break; + } + } while (1); + + if (zfpm_writes_pending ()) + zfpm_write_on (); + + return 0; +} + +/* + * zfpm_connect_cb + */ +static int +zfpm_connect_cb (struct thread *t) +{ + int sock, ret; + struct sockaddr_in serv; + + assert (zfpm_g->t_connect); + zfpm_g->t_connect = NULL; + assert (zfpm_g->state == ZFPM_STATE_ACTIVE); + + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + zfpm_debug ("Failed to create socket for connect(): %s", strerror(errno)); + zfpm_g->stats.connect_no_sock++; + return 0; + } + + set_nonblocking(sock); + + /* Make server socket. */ + memset (&serv, 0, sizeof (serv)); + serv.sin_family = AF_INET; + serv.sin_port = htons (zfpm_g->fpm_port); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* + * Connect to the FPM. + */ + zfpm_g->connect_calls++; + zfpm_g->stats.connect_calls++; + zfpm_g->last_connect_call_time = zfpm_get_time (); + + ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); + if (ret >= 0) + { + zfpm_g->sock = sock; + zfpm_connection_up ("connect succeeded"); + return 1; + } + + if (errno == EINPROGRESS) + { + zfpm_g->sock = sock; + zfpm_read_on (); + zfpm_write_on (); + zfpm_set_state (ZFPM_STATE_CONNECTING, "async connect in progress"); + return 0; + } + + zlog_info ("can't connect to FPM %d: %s", sock, safe_strerror (errno)); + close (sock); + + /* + * Restart timer for retrying connection. + */ + zfpm_start_connect_timer ("connect() failed"); + return 0; +} + +/* + * zfpm_set_state + * + * Move state machine into the given state. + */ +static void +zfpm_set_state (zfpm_state_t state, const char *reason) +{ + zfpm_state_t cur_state = zfpm_g->state; + + if (!reason) + reason = "Unknown"; + + if (state == cur_state) + return; + + zfpm_debug("beginning state transition %s -> %s. Reason: %s", + zfpm_state_to_str (cur_state), zfpm_state_to_str (state), + reason); + + switch (state) { + + case ZFPM_STATE_IDLE: + assert (cur_state == ZFPM_STATE_ESTABLISHED); + break; + + case ZFPM_STATE_ACTIVE: + assert (cur_state == ZFPM_STATE_IDLE || + cur_state == ZFPM_STATE_CONNECTING); + assert (zfpm_g->t_connect); + break; + + case ZFPM_STATE_CONNECTING: + assert (zfpm_g->sock); + assert (cur_state == ZFPM_STATE_ACTIVE); + assert (zfpm_g->t_read); + assert (zfpm_g->t_write); + break; + + case ZFPM_STATE_ESTABLISHED: + assert (cur_state == ZFPM_STATE_ACTIVE || + cur_state == ZFPM_STATE_CONNECTING); + assert (zfpm_g->sock); + assert (zfpm_g->t_read); + assert (zfpm_g->t_write); + break; + } + + zfpm_g->state = state; +} + +/* + * zfpm_calc_connect_delay + * + * Returns the number of seconds after which we should attempt to + * reconnect to the FPM. + */ +static long +zfpm_calc_connect_delay (void) +{ + time_t elapsed; + + /* + * Return 0 if this is our first attempt to connect. + */ + if (zfpm_g->connect_calls == 0) + { + return 0; + } + + elapsed = zfpm_get_elapsed_time (zfpm_g->last_connect_call_time); + + if (elapsed > ZFPM_CONNECT_RETRY_IVL) { + return 0; + } + + return ZFPM_CONNECT_RETRY_IVL - elapsed; +} + +/* + * zfpm_start_connect_timer + */ +static void +zfpm_start_connect_timer (const char *reason) +{ + long delay_secs; + + assert (!zfpm_g->t_connect); + assert (zfpm_g->sock < 0); + + assert(zfpm_g->state == ZFPM_STATE_IDLE || + zfpm_g->state == ZFPM_STATE_ACTIVE || + zfpm_g->state == ZFPM_STATE_CONNECTING); + + delay_secs = zfpm_calc_connect_delay(); + zfpm_debug ("scheduling connect in %ld seconds", delay_secs); + + THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_connect, zfpm_connect_cb, 0, + delay_secs); + zfpm_set_state (ZFPM_STATE_ACTIVE, reason); +} + +/* + * zfpm_is_enabled + * + * Returns TRUE if the zebra FPM module has been enabled. + */ +static inline int +zfpm_is_enabled (void) +{ + return zfpm_g->enabled; +} + +/* + * zfpm_conn_is_up + * + * Returns TRUE if the connection to the FPM is up. + */ +static inline int +zfpm_conn_is_up (void) +{ + if (zfpm_g->state != ZFPM_STATE_ESTABLISHED) + return 0; + + assert (zfpm_g->sock >= 0); + + return 1; +} + +/* + * zfpm_trigger_update + * + * The zebra code invokes this function to indicate that we should + * send an update to the FPM about the given route_node. + */ +void +zfpm_trigger_update (struct route_node *rn, const char *reason) +{ + rib_dest_t *dest; + char buf[INET6_ADDRSTRLEN]; + + /* + * Ignore if the connection is down. We will update the FPM about + * all destinations once the connection comes up. + */ + if (!zfpm_conn_is_up ()) + return; + + dest = rib_dest_from_rnode (rn); + + /* + * Ignore the trigger if the dest is not in a table that we would + * send to the FPM. + */ + if (!zfpm_is_table_for_fpm (rib_dest_table (dest))) + { + zfpm_g->stats.non_fpm_table_triggers++; + return; + } + + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) { + zfpm_g->stats.redundant_triggers++; + return; + } + + if (reason) + { + zfpm_debug ("%s/%d triggering update to FPM - Reason: %s", + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)), + rn->p.prefixlen, reason); + } + + SET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + TAILQ_INSERT_TAIL (&zfpm_g->dest_q, dest, fpm_q_entries); + zfpm_g->stats.updates_triggered++; + + /* + * Make sure that writes are enabled. + */ + if (zfpm_g->t_write) + return; + + zfpm_write_on (); +} + +/* + * zfpm_stats_timer_cb + */ +static int +zfpm_stats_timer_cb (struct thread *t) +{ + assert (zfpm_g->t_stats); + zfpm_g->t_stats = NULL; + + /* + * Remember the stats collected in the last interval for display + * purposes. + */ + zfpm_stats_copy (&zfpm_g->stats, &zfpm_g->last_ivl_stats); + + /* + * Add the current set of stats into the cumulative statistics. + */ + zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats, + &zfpm_g->cumulative_stats); + + /* + * Start collecting stats afresh over the next interval. + */ + zfpm_stats_reset (&zfpm_g->stats); + + zfpm_start_stats_timer (); + + return 0; +} + +/* + * zfpm_stop_stats_timer + */ +static void +zfpm_stop_stats_timer (void) +{ + if (!zfpm_g->t_stats) + return; + + zfpm_debug ("Stopping existing stats timer"); + THREAD_TIMER_OFF (zfpm_g->t_stats); +} + +/* + * zfpm_start_stats_timer + */ +void +zfpm_start_stats_timer (void) +{ + assert (!zfpm_g->t_stats); + + THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_stats, zfpm_stats_timer_cb, 0, + ZFPM_STATS_IVL_SECS); +} + +/* + * Helper macro for zfpm_show_stats() below. + */ +#define ZFPM_SHOW_STAT(counter) \ + do { \ + vty_out (vty, "%-40s %10lu %16lu%s", #counter, total_stats.counter, \ + zfpm_g->last_ivl_stats.counter, VTY_NEWLINE); \ + } while (0) + +/* + * zfpm_show_stats + */ +static void +zfpm_show_stats (struct vty *vty) +{ + zfpm_stats_t total_stats; + time_t elapsed; + + vty_out (vty, "%s%-40s %10s Last %2d secs%s%s", VTY_NEWLINE, "Counter", + "Total", ZFPM_STATS_IVL_SECS, VTY_NEWLINE, VTY_NEWLINE); + + /* + * Compute the total stats up to this instant. + */ + zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats, + &total_stats); + + ZFPM_SHOW_STAT (connect_calls); + ZFPM_SHOW_STAT (connect_no_sock); + ZFPM_SHOW_STAT (read_cb_calls); + ZFPM_SHOW_STAT (write_cb_calls); + ZFPM_SHOW_STAT (write_calls); + ZFPM_SHOW_STAT (partial_writes); + ZFPM_SHOW_STAT (max_writes_hit); + ZFPM_SHOW_STAT (t_write_yields); + ZFPM_SHOW_STAT (nop_deletes_skipped); + ZFPM_SHOW_STAT (route_adds); + ZFPM_SHOW_STAT (route_dels); + ZFPM_SHOW_STAT (updates_triggered); + ZFPM_SHOW_STAT (non_fpm_table_triggers); + ZFPM_SHOW_STAT (redundant_triggers); + ZFPM_SHOW_STAT (dests_del_after_update); + ZFPM_SHOW_STAT (t_conn_down_starts); + ZFPM_SHOW_STAT (t_conn_down_dests_processed); + ZFPM_SHOW_STAT (t_conn_down_yields); + ZFPM_SHOW_STAT (t_conn_down_finishes); + ZFPM_SHOW_STAT (t_conn_up_starts); + ZFPM_SHOW_STAT (t_conn_up_dests_processed); + ZFPM_SHOW_STAT (t_conn_up_yields); + ZFPM_SHOW_STAT (t_conn_up_aborts); + ZFPM_SHOW_STAT (t_conn_up_finishes); + + if (!zfpm_g->last_stats_clear_time) + return; + + elapsed = zfpm_get_elapsed_time (zfpm_g->last_stats_clear_time); + + vty_out (vty, "%sStats were cleared %lu seconds ago%s", VTY_NEWLINE, + (unsigned long) elapsed, VTY_NEWLINE); +} + +/* + * zfpm_clear_stats + */ +static void +zfpm_clear_stats (struct vty *vty) +{ + if (!zfpm_is_enabled ()) + { + vty_out (vty, "The FPM module is not enabled...%s", VTY_NEWLINE); + return; + } + + zfpm_stats_reset (&zfpm_g->stats); + zfpm_stats_reset (&zfpm_g->last_ivl_stats); + zfpm_stats_reset (&zfpm_g->cumulative_stats); + + zfpm_stop_stats_timer (); + zfpm_start_stats_timer (); + + zfpm_g->last_stats_clear_time = zfpm_get_time(); + + vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE); +} + +/* + * show_zebra_fpm_stats + */ +DEFUN (show_zebra_fpm_stats, + show_zebra_fpm_stats_cmd, + "show zebra fpm stats", + SHOW_STR + "Zebra information\n" + "Forwarding Path Manager information\n" + "Statistics\n") +{ + zfpm_show_stats (vty); + return CMD_SUCCESS; +} + +/* + * clear_zebra_fpm_stats + */ +DEFUN (clear_zebra_fpm_stats, + clear_zebra_fpm_stats_cmd, + "clear zebra fpm stats", + CLEAR_STR + "Zebra information\n" + "Clear Forwarding Path Manager information\n" + "Statistics\n") +{ + zfpm_clear_stats (vty); + return CMD_SUCCESS; +} + +/** + * zfpm_init + * + * One-time initialization of the Zebra FPM module. + * + * @param[in] port port at which FPM is running. + * @param[in] enable TRUE if the zebra FPM module should be enabled + * + * Returns TRUE on success. + */ +int +zfpm_init (struct thread_master *master, int enable, uint16_t port) +{ + static int initialized = 0; + + if (initialized) { + return 1; + } + + initialized = 1; + + memset (zfpm_g, 0, sizeof (*zfpm_g)); + zfpm_g->master = master; + TAILQ_INIT(&zfpm_g->dest_q); + zfpm_g->sock = -1; + zfpm_g->state = ZFPM_STATE_IDLE; + + /* + * Netlink must currently be available for the Zebra-FPM interface + * to be enabled. + */ +#ifndef HAVE_NETLINK + enable = 0; +#endif + + zfpm_g->enabled = enable; + + zfpm_stats_init (&zfpm_g->stats); + zfpm_stats_init (&zfpm_g->last_ivl_stats); + zfpm_stats_init (&zfpm_g->cumulative_stats); + + install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd); + install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd); + + if (!enable) { + return 1; + } + + if (!port) + port = FPM_DEFAULT_PORT; + + zfpm_g->fpm_port = port; + + zfpm_g->obuf = stream_new (ZFPM_OBUF_SIZE); + zfpm_g->ibuf = stream_new (ZFPM_IBUF_SIZE); + + zfpm_start_stats_timer (); + zfpm_start_connect_timer ("initialized"); + + return 1; +} diff --git a/zebra/zebra_fpm.h b/zebra/zebra_fpm.h new file mode 100644 index 00000000..44dec028 --- /dev/null +++ b/zebra/zebra_fpm.h @@ -0,0 +1,34 @@ +/* + * Header file exported by the zebra FPM module to zebra. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_FPM_H +#define _ZEBRA_FPM_H + +/* + * Externs. + */ +extern int zfpm_init (struct thread_master *master, int enable, uint16_t port); +extern void zfpm_trigger_update (struct route_node *rn, const char *reason); + +#endif /* _ZEBRA_FPM_H */ diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c new file mode 100644 index 00000000..b5f2b760 --- /dev/null +++ b/zebra/zebra_fpm_netlink.c @@ -0,0 +1,490 @@ +/* + * Code for encoding/decoding FPM messages that are in netlink format. + * + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "log.h" +#include "rib.h" + +#include "rt_netlink.h" + +#include "zebra_fpm_private.h" + +/* + * addr_to_a + * + * Returns string representation of an address of the given AF. + */ +static inline const char * +addr_to_a (u_char af, void *addr) +{ + if (!addr) + return ""; + + switch (af) + { + + case AF_INET: + return inet_ntoa (*((struct in_addr *) addr)); + +#ifdef HAVE_IPV6 + case AF_INET6: + return inet6_ntoa (*((struct in6_addr *) addr)); +#endif + + default: + return ""; + } +} + +/* + * prefix_addr_to_a + * + * Convience wrapper that returns a human-readable string for the + * address in a prefix. + */ +static const char * +prefix_addr_to_a (struct prefix *prefix) +{ + if (!prefix) + return ""; + + return addr_to_a (prefix->family, &prefix->u.prefix); +} + +/* + * af_addr_size + * + * The size of an address in a given address family. + */ +static size_t +af_addr_size (u_char af) +{ + switch (af) + { + + case AF_INET: + return 4; + +#ifdef HAVE_IPV6 + case AF_INET6: + return 16; +#endif + + default: + assert(0); + return 16; + } +} + +/* + * netlink_nh_info_t + * + * Holds information about a single nexthop for netlink. These info + * structures are transient and may contain pointers into rib + * data structures for convenience. + */ +typedef struct netlink_nh_info_t_ +{ + uint32_t if_index; + union g_addr *gateway; + + /* + * Information from the struct nexthop from which this nh was + * derived. For debug purposes only. + */ + int recursive; + enum nexthop_types_t type; +} netlink_nh_info_t; + +/* + * netlink_route_info_t + * + * A structure for holding information for a netlink route message. + */ +typedef struct netlink_route_info_t_ +{ + uint16_t nlmsg_type; + u_char rtm_type; + uint32_t rtm_table; + u_char rtm_protocol; + u_char af; + struct prefix *prefix; + uint32_t *metric; + int num_nhs; + + /* + * Nexthop structures. We keep things simple for now by enforcing a + * maximum of 64 in case MULTIPATH_NUM is 0; + */ + netlink_nh_info_t nhs[MAX (MULTIPATH_NUM, 64)]; + union g_addr *pref_src; +} netlink_route_info_t; + +/* + * netlink_route_info_add_nh + * + * Add information about the given nexthop to the given route info + * structure. + * + * Returns TRUE if a nexthop was added, FALSE otherwise. + */ +static int +netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop, + int recursive) +{ + netlink_nh_info_t nhi; + union g_addr *src; + + memset (&nhi, 0, sizeof (nhi)); + src = NULL; + + if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs)) + return 0; + + nhi.recursive = recursive; + nhi.type = nexthop->type; + nhi.if_index = nexthop->ifindex; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + nhi.gateway = &nexthop->gate; + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + nhi.gateway = &nexthop->gate; + } +#endif /* HAVE_IPV6 */ + + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + if (!nhi.gateway && nhi.if_index == 0) + return 0; + + /* + * We have a valid nhi. Copy the structure over to the route_info. + */ + ri->nhs[ri->num_nhs] = nhi; + ri->num_nhs++; + + if (src && !ri->pref_src) + ri->pref_src = src; + + return 1; +} + +/* + * netlink_proto_from_route_type + */ +static u_char +netlink_proto_from_route_type (int type) +{ + switch (type) + { + case ZEBRA_ROUTE_KERNEL: + case ZEBRA_ROUTE_CONNECT: + return RTPROT_KERNEL; + + default: + return RTPROT_ZEBRA; + } +} + +/* + * netlink_route_info_fill + * + * Fill out the route information object from the given route. + * + * Returns TRUE on success and FALSE on failure. + */ +static int +netlink_route_info_fill (netlink_route_info_t *ri, int cmd, + rib_dest_t *dest, struct rib *rib) +{ + struct nexthop *nexthop, *tnexthop; + int recursing; + int discard; + + memset (ri, 0, sizeof (*ri)); + + ri->prefix = rib_dest_prefix (dest); + ri->af = rib_dest_af (dest); + + ri->nlmsg_type = cmd; + ri->rtm_table = rib_dest_vrf (dest)->id; + ri->rtm_protocol = RTPROT_UNSPEC; + + /* + * An RTM_DELROUTE need not be accompanied by any nexthops, + * particularly in our communication with the FPM. + */ + if (cmd == RTM_DELROUTE && !rib) + goto skip; + + if (rib) + ri->rtm_protocol = netlink_proto_from_route_type (rib->type); + + if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) + discard = 1; + else + discard = 0; + + if (cmd == RTM_NEWROUTE) + { + if (discard) + { + if (rib->flags & ZEBRA_FLAG_BLACKHOLE) + ri->rtm_type = RTN_BLACKHOLE; + else if (rib->flags & ZEBRA_FLAG_REJECT) + ri->rtm_type = RTN_UNREACHABLE; + else + assert (0); + } + else + ri->rtm_type = RTN_UNICAST; + } + + ri->metric = &rib->metric; + + if (discard) + { + goto skip; + } + + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + if (MULTIPATH_NUM != 0 && ri->num_nhs >= MULTIPATH_NUM) + break; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if ((cmd == RTM_NEWROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) + { + netlink_route_info_add_nh (ri, nexthop, recursing); + } + } + + /* If there is no useful nexthop then return. */ + if (ri->num_nhs == 0) + { + zfpm_debug ("netlink_encode_route(): No useful nexthop."); + return 0; + } + + skip: + return 1; +} + +/* + * netlink_route_info_encode + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +static int +netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf, + size_t in_buf_len) +{ + int bytelen; + int nexthop_num = 0; + size_t buf_offset; + netlink_nh_info_t *nhi; + + struct + { + struct nlmsghdr n; + struct rtmsg r; + char buf[1]; + } *req; + + req = (void *) in_buf; + + buf_offset = ((char *) req->buf) - ((char *) req); + + if (in_buf_len < buf_offset) { + assert(0); + return 0; + } + + memset (req, 0, buf_offset); + + bytelen = af_addr_size (ri->af); + + req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req->n.nlmsg_type = ri->nlmsg_type; + req->r.rtm_family = ri->af; + req->r.rtm_table = ri->rtm_table; + req->r.rtm_dst_len = ri->prefix->prefixlen; + req->r.rtm_protocol = ri->rtm_protocol; + req->r.rtm_scope = RT_SCOPE_UNIVERSE; + + addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen); + + req->r.rtm_type = ri->rtm_type; + + /* Metric. */ + if (ri->metric) + addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); + + if (ri->num_nhs == 0) + goto done; + + if (ri->num_nhs == 1) + { + nhi = &ri->nhs[0]; + + if (nhi->gateway) + { + addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway, + bytelen); + } + + if (nhi->if_index) + { + addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index); + } + + goto done; + + } + + /* + * Multipath case. + */ + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH (0); + rtnh = RTA_DATA (rta); + + for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) + { + nhi = &ri->nhs[nexthop_num]; + + rtnh->rtnh_len = sizeof (*rtnh); + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rtnh->rtnh_ifindex = 0; + rta->rta_len += rtnh->rtnh_len; + + if (nhi->gateway) + { + rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + } + + if (nhi->if_index) + { + rtnh->rtnh_ifindex = nhi->if_index; + } + + rtnh = RTNH_NEXT (rtnh); + } + + assert (rta->rta_len > RTA_LENGTH (0)); + addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + +done: + + if (ri->pref_src) + { + addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen); + } + + assert (req->n.nlmsg_len < in_buf_len); + return req->n.nlmsg_len; +} + +/* + * zfpm_log_route_info + * + * Helper function to log the information in a route_info structure. + */ +static void +zfpm_log_route_info (netlink_route_info_t *ri, const char *label) +{ + netlink_nh_info_t *nhi; + int i; + + zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label, + nl_msg_type_to_str (ri->nlmsg_type), + prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen, + nl_rtproto_to_str (ri->rtm_protocol), + ri->metric ? *ri->metric : 0); + + for (i = 0; i < ri->num_nhs; i++) + { + nhi = &ri->nhs[i]; + zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", + nhi->if_index, addr_to_a (ri->af, nhi->gateway), + nhi->recursive ? "yes" : "no", + nexthop_type_to_str (nhi->type)); + } +} + +/* + * zfpm_netlink_encode_route + * + * Create a netlink message corresponding to the given route in the + * given buffer space. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +int +zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, + char *in_buf, size_t in_buf_len) +{ + netlink_route_info_t ri_space, *ri; + + ri = &ri_space; + + if (!netlink_route_info_fill (ri, cmd, dest, rib)) + return 0; + + zfpm_log_route_info (ri, __FUNCTION__); + + return netlink_route_info_encode (ri, in_buf, in_buf_len); +} diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h new file mode 100644 index 00000000..809a70a4 --- /dev/null +++ b/zebra/zebra_fpm_private.h @@ -0,0 +1,56 @@ +/* + * Private header file for the zebra FPM module. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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. + * + * GNU Zebra 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 General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_FPM_PRIVATE_H +#define _ZEBRA_FPM_PRIVATE_H + +#include "zebra/debug.h" + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + +#define zfpm_debug(...) \ + do { \ + if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " __VA_ARGS__); \ + } while(0) + +#elif defined __GNUC__ + +#define zfpm_debug(_args...) \ + do { \ + if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " _args); \ + } while(0) + +#else +static inline void zfpm_debug(const char *format, ...) { return; } +#endif + + +/* + * Externs + */ +extern int +zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, + char *in_buf, size_t in_buf_len); + +#endif /* _ZEBRA_FPM_PRIVATE_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0e29e616..faa314f5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -40,6 +40,7 @@ #include "zebra/zserv.h" #include "zebra/redistribute.h" #include "zebra/debug.h" +#include "zebra/zebra_fpm.h" /* Default rtm_table for all clients */ extern struct zebra_t zebrad; @@ -67,13 +68,74 @@ static const struct [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110}, [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, + [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 0}, + [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 0}, + [ZEBRA_ROUTE_BATMAN] = {ZEBRA_ROUTE_BATMAN, 0}, [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, /* no entry/default: 150 */ }; - + /* Vector for routing table. */ static vector vrf_vector; +/* RPF lookup behaviour */ +static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; + +static void +_rnode_zlog(const char *_func, struct route_node *rn, int priority, + const char *msgfmt, ...) +{ + char buf[INET6_ADDRSTRLEN + 4], *bptr; + char msgbuf[512]; + va_list ap; + + va_start(ap, msgfmt); + vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); + va_end(ap); + + if (rn) + { + rib_table_info_t *info = rn->table->info; + + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); + bptr = buf + strlen(buf); + snprintf(bptr, buf + sizeof(buf) - bptr, "/%d%s", rn->p.prefixlen, + info->safi == SAFI_MULTICAST ? " (MRIB)" : ""); + } + else + { + snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); + } + + zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf); +} + +#define rnode_debug(node, ...) \ + _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__) +#define rnode_info(node, ...) \ + _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) + +/* + * vrf_table_create + */ +static void +vrf_table_create (struct vrf *vrf, afi_t afi, safi_t safi) +{ + rib_table_info_t *info; + struct route_table *table; + + assert (!vrf->table[afi][safi]); + + table = route_table_init (); + vrf->table[afi][safi] = table; + + info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info)); + info->vrf = vrf; + info->afi = afi; + info->safi = safi; + table->info = info; +} + /* Allocate new VRF. */ static struct vrf * vrf_alloc (const char *name) @@ -87,12 +149,12 @@ vrf_alloc (const char *name) vrf->name = XSTRDUP (MTYPE_VRF_NAME, name); /* Allocate routing table and static table. */ - vrf->table[AFI_IP][SAFI_UNICAST] = route_table_init (); - vrf->table[AFI_IP6][SAFI_UNICAST] = route_table_init (); + vrf_table_create (vrf, AFI_IP, SAFI_UNICAST); + vrf_table_create (vrf, AFI_IP6, SAFI_UNICAST); vrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init (); vrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init (); - vrf->table[AFI_IP][SAFI_MULTICAST] = route_table_init (); - vrf->table[AFI_IP6][SAFI_MULTICAST] = route_table_init (); + vrf_table_create (vrf, AFI_IP, SAFI_MULTICAST); + vrf_table_create (vrf, AFI_IP6, SAFI_MULTICAST); vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); @@ -133,6 +195,9 @@ vrf_table (afi_t afi, safi_t safi, u_int32_t id) if (! vrf) return NULL; + if( afi >= AFI_MAX || safi >= SAFI_MAX ) + return NULL; + return vrf->table[afi][safi]; } @@ -146,23 +211,57 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) if (! vrf) return NULL; + if( afi >= AFI_MAX || safi >= SAFI_MAX ) + return NULL; + return vrf->stable[afi][safi]; } - -/* Add nexthop to the end of the list. */ + +/* + * nexthop_type_to_str + */ +const char * +nexthop_type_to_str (enum nexthop_types_t nh_type) +{ + static const char *desc[] = { + "none", + "Directly connected", + "Interface route", + "IPv4 nexthop", + "IPv4 nexthop with ifindex", + "IPv4 nexthop with ifname", + "IPv6 nexthop", + "IPv6 nexthop with ifindex", + "IPv6 nexthop with ifname", + "Null0 nexthop", + }; + + if (nh_type >= ZEBRA_NUM_OF (desc)) + return ""; + + return desc[nh_type]; +} + +/* Add nexthop to the end of a nexthop list. */ static void -nexthop_add (struct rib *rib, struct nexthop *nexthop) +_nexthop_add (struct nexthop **target, struct nexthop *nexthop) { struct nexthop *last; - for (last = rib->nexthop; last && last->next; last = last->next) + for (last = *target; last && last->next; last = last->next) ; if (last) last->next = nexthop; else - rib->nexthop = nexthop; + *target = nexthop; nexthop->prev = last; +} +/* Add nexthop to the end of a rib node's nexthop list */ +static void +nexthop_add (struct rib *rib, struct nexthop *nexthop) +{ + _nexthop_add(&rib->nexthop, nexthop); rib->nexthop_num++; } @@ -179,15 +278,32 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num--; } +static void nexthops_free(struct nexthop *nexthop); + /* Free nexthop. */ static void nexthop_free (struct nexthop *nexthop) { if (nexthop->ifname) XFREE (0, nexthop->ifname); + if (nexthop->resolved) + nexthops_free(nexthop->resolved); XFREE (MTYPE_NEXTHOP, nexthop); } +/* Frees a list of nexthops */ +static void +nexthops_free (struct nexthop *nexthop) +{ + struct nexthop *nh, *next; + + for (nh = nexthop; nh; nh = next) + { + next = nh->next; + nexthop_free (nh); + } +} + struct nexthop * nexthop_ifindex_add (struct rib *rib, unsigned int ifindex) { @@ -312,6 +428,24 @@ nexthop_blackhole_add (struct rib *rib) return nexthop; } +/* This method checks whether a recursive nexthop has at + * least one resolved nexthop in the fib. + */ +int +nexthop_has_fib_child(struct nexthop *nexthop) +{ + struct nexthop *nh; + + if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + return 0; + + for (nh = nexthop->resolved; nh; nh = nh->next) + if (CHECK_FLAG (nh->flags, NEXTHOP_FLAG_FIB)) + return 1; + + return 0; +} + /* If force flag is not set, do not modify falgs at all for uninstall the route from FIB. */ static int @@ -322,13 +456,19 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, struct route_table *table; struct route_node *rn; struct rib *match; + int resolved; struct nexthop *newhop; + struct nexthop *resolved_hop; if (nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = 0; if (set) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + } /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -351,7 +491,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, return 0; /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -372,6 +512,12 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } else { + /* If the longest prefix match for the nexthop yields + * a blackhole, mark it as inactive. */ + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE) + || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT)) + return 0; + if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ @@ -383,6 +529,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { + resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -390,15 +537,55 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop->rtype = newhop->type; - if (newhop->type == NEXTHOP_TYPE_IPV4 || - newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - nexthop->rgate.ipv4 = newhop->gate.ipv4; + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* If the resolving route specifies a gateway, use it */ + if (newhop->type == NEXTHOP_TYPE_IPV4 + || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME) + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } + + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. */ if (newhop->type == NEXTHOP_TYPE_IFINDEX - || newhop->type == NEXTHOP_TYPE_IFNAME - || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - nexthop->rifindex = newhop->ifindex; + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + resolved_hop->ifindex = newhop->ifindex; + } + + _nexthop_add(&nexthop->resolved, resolved_hop); } + resolved = 1; + } + return resolved; + } + else if (match->type == ZEBRA_ROUTE_OLSR) + { + for (newhop = match->nexthop; newhop; newhop = newhop->next) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) + && newhop->type == NEXTHOP_TYPE_IFINDEX) + { + if (nexthop->type == NEXTHOP_TYPE_IPV4) + nexthop->ifindex = newhop->ifindex; return 1; } return 0; @@ -423,13 +610,19 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct route_table *table; struct route_node *rn; struct rib *match; + int resolved; struct nexthop *newhop; + struct nexthop *resolved_hop; if (nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = 0; if (set) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + } /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); @@ -452,7 +645,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, return 0; /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -473,6 +666,12 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } else { + /* If the longest prefix match for the nexthop yields + * a blackhole, mark it as inactive. */ + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE) + || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT)) + return 0; + if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ @@ -485,6 +684,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { + resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -492,17 +692,48 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop->rtype = newhop->type; + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* See nexthop_active_ipv4 for a description how the + * resolved nexthop is constructed. */ if (newhop->type == NEXTHOP_TYPE_IPV6 || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - nexthop->rgate.ipv6 = newhop->gate.ipv6; + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } + if (newhop->type == NEXTHOP_TYPE_IFINDEX - || newhop->type == NEXTHOP_TYPE_IFNAME - || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX - || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - nexthop->rifindex = newhop->ifindex; + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + resolved_hop->ifindex = newhop->ifindex; + } + + _nexthop_add(&nexthop->resolved, resolved_hop); } + resolved = 1; + } + return resolved; + } + else if (match->type == ZEBRA_ROUTE_OLSR) + { + for (newhop = match->nexthop; newhop; newhop = newhop->next) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) + && newhop->type == NEXTHOP_TYPE_IFINDEX) + { + if (nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = newhop->ifindex; return 1; } return 0; @@ -518,32 +749,28 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, #endif /* HAVE_IPV6 */ struct rib * -rib_match_ipv4 (struct in_addr addr) +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, + struct route_node **rn_out) { - struct prefix_ipv4 p; struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return 0; - memset (&p, 0, sizeof (struct prefix_ipv4)); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = addr; - - rn = route_node_match (table, (struct prefix *) &p); + rn = route_node_match_ipv4 (table, &addr); while (rn) { route_unlock_node (rn); - + /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -553,8 +780,7 @@ rib_match_ipv4 (struct in_addr addr) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP))) { do { rn = rn->parent; @@ -564,28 +790,107 @@ rib_match_ipv4 (struct in_addr addr) } else { - if (match->type == ZEBRA_ROUTE_CONNECT) - /* Directly point connected route. */ - return match; - else + if (match->type != ZEBRA_ROUTE_CONNECT) { - for (newhop = match->nexthop; newhop; newhop = newhop->next) + int found = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) - return match; - return NULL; + { + found = 1; + break; + } + if (!found) + return NULL; } + + if (rn_out) + *rn_out = rn; + return match; } } return NULL; } +struct rib * +rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out) +{ + struct rib *rib = NULL, *mrib = NULL, *urib = NULL; + struct route_node *m_rn = NULL, *u_rn = NULL; + int skip_bgp = 0; /* bool */ + + switch (ipv4_multicast_mode) + { + case MCAST_MRIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out); + case MCAST_URIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out); + case MCAST_NO_CONFIG: + case MCAST_MIX_MRIB_FIRST: + rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + if (!mrib) + rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + break; + case MCAST_MIX_DISTANCE: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = urib->distance < mrib->distance ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + case MCAST_MIX_PFXLEN: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + } + + if (rn_out) + *rn_out = (rib == mrib) ? m_rn : u_rn; + + if (IS_ZEBRA_DEBUG_RIB) + { + char buf[BUFSIZ]; + inet_ntop (AF_INET, &addr, buf, BUFSIZ); + + zlog_debug("%s: %s: found %s, using %s", + __func__, buf, + mrib ? (urib ? "MRIB+URIB" : "MRIB") : + urib ? "URIB" : "nothing", + rib == urib ? "URIB" : rib == mrib ? "MRIB" : "none"); + } + return rib; +} + +void +multicast_mode_ipv4_set (enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, mode); + ipv4_multicast_mode = mode; +} + +enum multicast_mode +multicast_mode_ipv4_get (void) +{ + return ipv4_multicast_mode; +} + struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -601,7 +906,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) /* Unlock node. */ route_unlock_node (rn); - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -615,7 +920,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) if (match->type == ZEBRA_ROUTE_CONNECT) return match; - for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) return match; @@ -640,7 +945,9 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; + int nexthops_active; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -658,7 +965,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) route_unlock_node (rn); /* Find out if a "selected" RR for the discovered RIB entry exists ever. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -674,26 +981,25 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) return ZEBRA_RIB_FOUND_CONNECTED; /* Ok, we have a cood candidate, let's check it's nexthop list... */ - for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next) + nexthops_active = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) - { - /* We are happy with either direct or recursive hexthop */ - if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate) || - nexthop->rgate.ipv4.s_addr == sockunion2ip (qgate)) - return ZEBRA_RIB_FOUND_EXACT; - else { + nexthops_active = 1; + if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate)) + return ZEBRA_RIB_FOUND_EXACT; if (IS_ZEBRA_DEBUG_RIB) - { - char gate_buf[INET_ADDRSTRLEN], rgate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN]; - inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, rgate_buf, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &sockunion2ip (qgate), qgate_buf, INET_ADDRSTRLEN); - zlog_debug ("%s: qgate == %s, gate == %s, rgate == %s", __func__, qgate_buf, gate_buf, rgate_buf); - } - return ZEBRA_RIB_FOUND_NOGATE; + { + char gate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN); + inet_ntop (AF_INET, &sockunion2ip(qgate), qgate_buf, INET_ADDRSTRLEN); + zlog_debug ("%s: qgate == %s, %s == %s", __func__, + qgate_buf, recursing ? "rgate" : "gate", gate_buf); + } } - } + + if (nexthops_active) + return ZEBRA_RIB_FOUND_NOGATE; return ZEBRA_RIB_NOTFOUND; } @@ -706,7 +1012,8 @@ rib_match_ipv6 (struct in6_addr *addr) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing; /* Lookup table. */ table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); @@ -725,7 +1032,7 @@ rib_match_ipv6 (struct in6_addr *addr) route_unlock_node (rn); /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -751,7 +1058,7 @@ rib_match_ipv6 (struct in6_addr *addr) return match; else { - for (newhop = match->nexthop; newhop; newhop = newhop->next) + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; @@ -779,6 +1086,7 @@ static unsigned nexthop_active_check (struct route_node *rn, struct rib *rib, struct nexthop *nexthop, int set) { + rib_table_info_t *info = rn->table->info; struct interface *ifp; route_map_result_t ret = RMAP_MATCH; extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1]; @@ -856,11 +1164,22 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) return 0; + /* XXX: What exactly do those checks do? Do we support + * e.g. IPv4 routes with IPv6 nexthops or vice versa? */ if (RIB_SYSTEM_ROUTE(rib) || (family == AFI_IP && rn->p.family != AF_INET) || (family == AFI_IP6 && rn->p.family != AF_INET6)) return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + /* The original code didn't determine the family correctly + * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi + * from the rib_table_info in those cases. + * Possibly it may be better to use only the rib_table_info + * in every case. + */ + if (!family) + family = info->afi; + rmap = 0; if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX && proto_rm[family][rib->type]) @@ -907,14 +1226,28 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) return rib->nexthop_active_num; } - + static void rib_install_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; + int recursing; + + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return; + } + /* + * Make sure we update the FPM any time we send new information to + * the kernel. + */ + zfpm_trigger_update (rn, "installing in kernel"); switch (PREFIX_FAMILY (&rn->p)) { case AF_INET: @@ -930,7 +1263,7 @@ rib_install_kernel (struct route_node *rn, struct rib *rib) /* This condition is never met, if we are using rt_socket.c */ if (ret < 0) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } @@ -940,7 +1273,22 @@ static int rib_uninstall_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; + int recursing; + + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return ret; + } + + /* + * Make sure we update the FPM any time we send new information to + * the kernel. + */ + zfpm_trigger_update (rn, "uninstalling from kernel"); switch (PREFIX_FAMILY (&rn->p)) { @@ -954,7 +1302,7 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) #endif /* HAVE_IPV6 */ } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); return ret; @@ -964,8 +1312,13 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) static void rib_uninstall (struct route_node *rn, struct rib *rib) { + rib_table_info_t *info = rn->table->info; + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "rib_uninstall"); + redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); @@ -975,6 +1328,64 @@ rib_uninstall (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *, struct rib *); +/* + * rib_can_delete_dest + * + * Returns TRUE if the given dest can be deleted from the table. + */ +static int +rib_can_delete_dest (rib_dest_t *dest) +{ + if (dest->routes) + { + return 0; + } + + /* + * Don't delete the dest if we have to update the FPM about this + * prefix. + */ + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM) || + CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM)) + return 0; + + return 1; +} + +/* + * rib_gc_dest + * + * Garbage collect the rib dest corresponding to the given route node + * if appropriate. + * + * Returns TRUE if the dest was deleted, FALSE otherwise. + */ +int +rib_gc_dest (struct route_node *rn) +{ + rib_dest_t *dest; + + dest = rib_dest_from_rnode (rn); + if (!dest) + return 0; + + if (!rib_can_delete_dest (dest)) + return 0; + + if (IS_ZEBRA_DEBUG_RIB) + rnode_debug (rn, "removing dest from table"); + + dest->rnode = NULL; + XFREE (MTYPE_RIB_DEST, dest); + rn->info = NULL; + + /* + * Release the one reference that we keep on the route node. + */ + route_unlock_node (rn); + return 1; +} + /* Core function for processing routing information base. */ static void rib_process (struct route_node *rn) @@ -985,21 +1396,16 @@ rib_process (struct route_node *rn) struct rib *select = NULL; struct rib *del = NULL; int installed = 0; - struct nexthop *nexthop = NULL; - char buf[INET6_ADDRSTRLEN]; - + struct nexthop *nexthop = NULL, *tnexthop; + int recursing; + rib_table_info_t *info; + assert (rn); - - if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - for (rib = rn->info; rib; rib = next) + info = rn->table->info; + + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - /* The next pointer is saved, because current pointer - * may be passed to rib_unlink() in the middle of iteration. - */ - next = rib->next; - /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { @@ -1015,9 +1421,8 @@ rib_process (struct route_node *rn) if (rib != fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); - rib_unlink (rn, rib); + rnode_debug (rn, "rn %p, removing rib %p", rn, rib); + rib_unlink (rn, rib); } else del = rib; @@ -1074,7 +1479,7 @@ rib_process (struct route_node *rn) /* metric tie-breaks equal distance */ if (rib->metric <= select->metric) select = rib; - } /* for (rib = rn->info; rib; rib = next) */ + } /* RNODE_FOREACH_RIB_SAFE */ /* After the cycle is finished, the following pointers will be set: * select --- the winner RIB entry, if any was found, otherwise NULL @@ -1087,10 +1492,13 @@ rib_process (struct route_node *rn) if (select && select == fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Updating existing route, select %p, fib %p", - __func__, buf, rn->p.prefixlen, select, fib); + rnode_debug (rn, "Updating existing route, select %p, fib %p", + select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "updating existing route"); + redistribute_delete (&rn->p, select); if (! RIB_SYSTEM_ROUTE (select)) rib_uninstall_kernel (rn, select); @@ -1111,7 +1519,7 @@ rib_process (struct route_node *rn) This makes sure the routes are IN the kernel. */ - for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { installed = 1; @@ -1130,8 +1538,11 @@ rib_process (struct route_node *rn) if (fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__, - buf, rn->p.prefixlen, fib); + rnode_debug (rn, "Removing existing route, fib %p", fib); + + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "removing existing route"); + redistribute_delete (&rn->p, fib); if (! RIB_SYSTEM_ROUTE (fib)) rib_uninstall_kernel (rn, fib); @@ -1148,8 +1559,11 @@ rib_process (struct route_node *rn) if (select) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf, - rn->p.prefixlen, select); + rnode_debug (rn, "Adding route, select %p", select); + + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "new route selected"); + /* Set real nexthop. */ nexthop_active_update (rn, select, 1); @@ -1163,14 +1577,18 @@ rib_process (struct route_node *rn) if (del) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf, - rn->p.prefixlen, del, rn); + rnode_debug (rn, "Deleting fib %p, rn %p", del, rn); rib_unlink (rn, del); } end: if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn); + rnode_debug (rn, "rn %p dequeued", rn); + + /* + * Check if the dest can be deleted now. + */ + rib_gc_dest (rn); } /* Take a list of route_node structs and return 1, if there was a record @@ -1189,8 +1607,9 @@ process_subq (struct list * subq, u_char qindex) rnode = listgetdata (lnode); rib_process (rnode); - if (rnode->info) /* The first RIB record is holding the flags bitmask. */ - UNSET_FLAG (((struct rib *)rnode->info)->rn_status, RIB_ROUTE_QUEUED(qindex)); + if (rnode->info) + UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex)); + #if 0 else { @@ -1223,7 +1642,9 @@ meta_queue_process (struct work_queue *dummy, void *data) return mq->size ? WQ_REQUEUE : WQ_SUCCESS; } -/* Map from rib types to queue type (priority) in meta queue */ +/* + * Map from rib types to queue type (priority) in meta queue + */ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_SYSTEM] = 4, [ZEBRA_ROUTE_KERNEL] = 0, @@ -1236,6 +1657,8 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_ISIS] = 2, [ZEBRA_ROUTE_BGP] = 3, [ZEBRA_ROUTE_HSLS] = 4, + [ZEBRA_ROUTE_OLSR] = 4, + [ZEBRA_ROUTE_BATMAN] = 4, [ZEBRA_ROUTE_BABEL] = 2, }; @@ -1246,32 +1669,29 @@ static void rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) { struct rib *rib; - char buf[INET6_ADDRSTRLEN]; - if (IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { u_char qindex = meta_queue_map[rib->type]; /* Invariant: at this point we always have rn->info set. */ - if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex))) + if (CHECK_FLAG (rib_dest_from_rnode (rn)->flags, + RIB_ROUTE_QUEUED (qindex))) { if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p is already queued in sub-queue %u", - __func__, buf, rn->p.prefixlen, rn, qindex); + rnode_debug (rn, "rn %p is already queued in sub-queue %u", + rn, qindex); continue; } - SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex)); + SET_FLAG (rib_dest_from_rnode (rn)->flags, RIB_ROUTE_QUEUED (qindex)); listnode_add (mq->subq[qindex], rn); route_lock_node (rn); mq->size++; if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: queued rn %p into sub-queue %u", - __func__, buf, rn->p.prefixlen, rn, qindex); + rnode_debug (rn, "queued rn %p into sub-queue %u", + rn, qindex); } } @@ -1279,14 +1699,10 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) static void rib_queue_add (struct zebra_t *zebra, struct route_node *rn) { - char buf[INET_ADDRSTRLEN]; assert (zebra && rn); - - if (IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN); /* Pointless to queue a route_node with no RIB entries to add or remove */ - if (!rn->info) + if (!rnode_to_ribs (rn)) { zlog_debug ("%s: called for route_node (%p, %d) with no ribs", __func__, rn, rn->lock); @@ -1295,7 +1711,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) } if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen); + rnode_info (rn, "work queue added"); assert (zebra); @@ -1319,7 +1735,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) rib_meta_queue_add (zebra->mq, rn); if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p queued", __func__, buf, rn->p.prefixlen, rn); + rnode_debug (rn, "rn %p queued", rn); return; } @@ -1395,17 +1811,16 @@ rib_queue_init (struct zebra_t *zebra) * |-> set RIB_ENTRY_REMOVE | * rib_delnode (RIB freed) * - * - * Queueing state for a route_node is kept in the head RIB entry, this - * state must be preserved as and when the head RIB entry of a - * route_node is changed by rib_unlink / rib_link. A small complication, - * but saves having to allocate a dedicated object for this. + * The 'info' pointer of a route_node points to a rib_dest_t + * ('dest'). Queueing state for a route_node is kept on the dest. The + * dest is created on-demand by rib_link() and is kept around at least + * as long as there are ribs hanging off it (@see rib_gc_dest()). * * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code): * * - route_nodes: refcounted by: - * - RIBs attached to route_node: - * - managed by: rib_link/unlink + * - dest attached to route_node: + * - managed by: rib_link/rib_gc_dest * - route_node processing queue * - managed by: rib_addqueue, rib_process. * @@ -1416,31 +1831,32 @@ static void rib_link (struct route_node *rn, struct rib *rib) { struct rib *head; - char buf[INET6_ADDRSTRLEN]; - + rib_dest_t *dest; + assert (rib && rn); - route_lock_node (rn); /* rn route table reference */ - if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); - head = rn->info; - if (head) + dest = rib_dest_from_rnode (rn); + if (!dest) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: new head, rn_status copied over", __func__, - buf, rn->p.prefixlen); + rnode_debug (rn, "adding dest to table"); + + dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t)); + route_lock_node (rn); /* rn route table reference */ + rn->info = dest; + dest->rnode = rn; + } + + head = dest->routes; + if (head) + { head->prev = rib; - /* Transfer the rn status flags to the new head RIB */ - rib->rn_status = head->rn_status; } rib->next = head; - rn->info = rib; + dest->routes = rib; rib_queue_add (&zebrad, rn); } @@ -1453,32 +1869,34 @@ rib_addnode (struct route_node *rn, struct rib *rib) if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, un-removed rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, un-removed rib %p", rn, rib); + UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED); return; } rib_link (rn, rib); } +/* + * rib_unlink + * + * Detach a rib structure from a route_node. + * + * Note that a call to rib_unlink() should be followed by a call to + * rib_gc_dest() at some point. This allows a rib_dest_t that is no + * longer required to be deleted. + */ static void rib_unlink (struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop, *next; - char buf[INET6_ADDRSTRLEN]; + rib_dest_t *dest; assert (rn && rib); if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); + + dest = rib_dest_from_rnode (rn); if (rib->next) rib->next->prev = rib->prev; @@ -1487,38 +1905,20 @@ rib_unlink (struct route_node *rn, struct rib *rib) rib->prev->next = rib->next; else { - rn->info = rib->next; - - if (rn->info) - { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: rn %p, rib %p, new head copy", - __func__, buf, rn->p.prefixlen, rn, rib); - rib->next->rn_status = rib->rn_status; - } + dest->routes = rib->next; } /* free RIB and nexthops */ - for (nexthop = rib->nexthop; nexthop; nexthop = next) - { - next = nexthop->next; - nexthop_free (nexthop); - } + nexthops_free(rib->nexthop); XFREE (MTYPE_RIB, rib); - route_unlock_node (rn); /* rn route table reference */ } static void rib_delnode (struct route_node *rn, struct rib *rib) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p, removing", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p, removing", rn, rib); SET_FLAG (rib->status, RIB_ENTRY_REMOVED); rib_queue_add (&zebrad, rn); } @@ -1561,7 +1961,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* If same type of route are installed, treat it as a implicit withdraw. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -1632,13 +2032,16 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, * question are passed as 1st and 2nd arguments. */ -void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib) +void _rib_dump (const char * func, + union prefix46constptr pp, const struct rib * rib) { - char straddr1[INET_ADDRSTRLEN], straddr2[INET_ADDRSTRLEN]; - struct nexthop *nexthop; + const struct prefix *p = pp.p; + char straddr[INET6_ADDRSTRLEN]; + struct nexthop *nexthop, *tnexthop; + int recursing; - inet_ntop (AF_INET, &p->prefix, straddr1, INET_ADDRSTRLEN); - zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr1, p->prefixlen); + inet_ntop (p->family, &p->u.prefix, straddr, INET6_ADDRSTRLEN); + zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr, p->prefixlen); zlog_debug ( "%s: refcnt == %lu, uptime == %lu, type == %u, table == %d", @@ -1665,21 +2068,21 @@ void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib rib->nexthop_active_num, rib->nexthop_fib_num ); - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN); - zlog_debug - ( - "%s: NH %s (%s) with flags %s%s%s", - func, - straddr1, - straddr2, - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""), - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""), - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "") - ); - } + + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + inet_ntop (p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN); + zlog_debug + ( + "%s: %s %s with flags %s%s%s", + func, + (recursing ? " NH" : "NH"), + straddr, + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""), + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""), + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "") + ); + } zlog_debug ("%s: dump complete", func); } @@ -1717,7 +2120,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) route_unlock_node (rn); /* let's go */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { zlog_debug ( @@ -1728,7 +2131,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"), (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected") ); - rib_dump (__func__, p, rib); + rib_dump (p, rib); } } @@ -1764,7 +2167,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) * RIBQ record already on head. This is necessary for proper revalidation * of the rest of the RIB. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && ! RIB_SYSTEM_ROUTE (rib)) @@ -1775,7 +2178,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) char buf[INET_ADDRSTRLEN]; inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN); zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen); - rib_dump (__func__, (struct prefix_ipv4 *)&rn->p, rib); + rib_dump (&rn->p, rib); } rib_uninstall (rn, rib); } @@ -1816,7 +2219,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) /* If same type of route are installed, treat it as a implicit withdraw. */ - for (same = rn->info; same; same = same->next) + RNODE_FOREACH_RIB (rn, same) { if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) continue; @@ -1837,7 +2240,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", __func__, rn, rib); - rib_dump (__func__, p, rib); + rib_dump (p, rib); } /* Free implicit route.*/ @@ -1847,7 +2250,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", __func__, rn, same); - rib_dump (__func__, p, same); + rib_dump (p, same); } rib_delnode (rn, same); } @@ -1866,7 +2269,8 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf1[INET_ADDRSTRLEN]; char buf2[INET_ADDRSTRLEN]; @@ -1878,12 +2282,20 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Apply mask. */ apply_mask_ipv4 (p); - if (IS_ZEBRA_DEBUG_KERNEL && gate) - zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, - inet_ntoa (*gate), - ifindex); + if (IS_ZEBRA_DEBUG_KERNEL) + { + if (gate) + zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen, + inet_ntoa (*gate), + ifindex); + else + zlog_debug ("rib_delete_ipv4(): route delete %s/%d ifindex %d", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen, + ifindex); + } /* Lookup route node. */ rn = route_node_lookup (table, (struct prefix *) p); @@ -1907,7 +2319,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, } /* Lookup same type route. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -1933,16 +2345,23 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, break; } /* Make sure that the route found has the same gateway. */ - else if (gate == NULL || - ((nexthop = rib->nexthop) && - (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate) || - IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate)))) + else { - same = rib; - break; - } + if (gate == NULL) + { + same = rib; + break; + } + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + if (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate)) + { + same = rib; + break; + } + if (same) + break; + } } - /* If same type of route can't be found and this message is from kernel. */ if (! same) @@ -1984,23 +2403,23 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, route_unlock_node (rn); return 0; } - + /* Install static route into rib. */ static void -static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_install_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct rib *rib; struct route_node *rn; struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; /* Lookup existing route */ rn = route_node_get (table, p); - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2079,7 +2498,7 @@ static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si) /* Uninstall static route from RIB. */ static void -static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_uninstall_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct route_node *rn; struct rib *rib; @@ -2087,7 +2506,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; @@ -2096,7 +2515,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) if (! rn) return; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2138,10 +2557,10 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) route_unlock_node (rn); } -/* Add static route into static route configuration. */ int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id) +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2152,7 +2571,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2186,7 +2605,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, /* Distance changed. */ if (update) - static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id); + static_delete_ipv4_safi (safi, p, gate, ifname, update->distance, vrf_id); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4)); @@ -2228,15 +2647,14 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, si->next = cp; /* Install into rib. */ - static_install_ipv4 (p, si); + static_install_ipv4 (safi, p, si); return 1; } -/* Delete static route from static route configuration. */ int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id) +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2244,7 +2662,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2276,7 +2694,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, } /* Install into rib. */ - static_uninstall_ipv4 (p, si); + static_uninstall_ipv4 (safi, p, si); /* Unlink static route from linked list. */ if (si->prev) @@ -2297,29 +2715,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, return 1; } - #ifdef HAVE_IPV6 -static int -rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, - struct in6_addr *gate, unsigned int ifindex, int table) -{ - if (type == ZEBRA_ROUTE_CONNECT && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)) { -#if defined (MUSICA) || defined (LINUX) - /* IN6_IS_ADDR_V4COMPAT(&p->prefix) */ - if (p->prefixlen == 96) - return 0; -#endif /* MUSICA */ - return 1; - } - if (type == ZEBRA_ROUTE_KERNEL && IN6_IS_ADDR_UNSPECIFIED (&p->prefix) - && p->prefixlen == 96 && gate && IN6_IS_ADDR_UNSPECIFIED (gate)) - { - kernel_delete_ipv6_old (p, gate, ifindex, 0, table); - return 1; - } - return 0; -} - int rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, @@ -2346,16 +2742,12 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) distance = 200; - /* Filter bogus route. */ - if (rib_bogus_ipv6 (type, p, gate, ifindex, 0)) - return 0; - /* Lookup route node.*/ rn = route_node_get (table, (struct prefix *) p); /* If same type of route are installed, treat it as a implicit withdraw. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2405,10 +2797,24 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, /* Link new rib to node.*/ rib_addnode (rn, rib); + if (IS_ZEBRA_DEBUG_RIB) + { + zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", + __func__, rn, rib); + rib_dump (p, rib); + } /* Free implicit route.*/ if (same) + { + if (IS_ZEBRA_DEBUG_RIB) + { + zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", + __func__, rn, same); + rib_dump (p, same); + } rib_delnode (rn, same); + } route_unlock_node (rn); return 0; @@ -2424,7 +2830,8 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; @@ -2458,7 +2865,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, } /* Lookup same type route. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2484,14 +2891,22 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, break; } /* Make sure that the route found has the same gateway. */ - else if (gate == NULL || - ((nexthop = rib->nexthop) && - (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) || - IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate)))) - { - same = rib; - break; - } + else + { + if (gate == NULL) + { + same = rib; + break; + } + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate)) + { + same = rib; + break; + } + if (same) + break; + } } /* If same type of route can't be found and this message is from @@ -2535,7 +2950,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, route_unlock_node (rn); return 0; } - + /* Install static route into rib. */ static void static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) @@ -2551,7 +2966,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) /* Lookup existing route */ rn = route_node_get (table, p); - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2588,6 +3003,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) rib->type = ZEBRA_ROUTE_STATIC; rib->distance = si->distance; rib->metric = 0; + rib->table = zebrad.rtm_table_default; rib->nexthop_num = 0; switch (si->type) @@ -2648,7 +3064,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si) if (! rn) return; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2833,7 +3249,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, return 1; } #endif /* HAVE_IPV6 */ - + /* RIB update function. */ void rib_update (void) @@ -2844,17 +3260,17 @@ rib_update (void) table = vrf_table (AFI_IP, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - if (rn->info) + if (rnode_to_ribs (rn)) rib_queue_add (&zebrad, rn); table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - if (rn->info) + if (rnode_to_ribs (rn)) rib_queue_add (&zebrad, rn); } - + /* Remove all routes which comes from non main table. */ static void rib_weed_table (struct route_table *table) @@ -2865,10 +3281,8 @@ rib_weed_table (struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2885,7 +3299,7 @@ rib_weed_tables (void) rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); } - + /* Delete self installed routes after zebra is relaunched. */ static void rib_sweep_table (struct route_table *table) @@ -2897,10 +3311,8 @@ rib_sweep_table (struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2933,9 +3345,8 @@ rib_score_proto_table (u_char proto, struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; if (rib->type == proto) @@ -2961,15 +3372,21 @@ static void rib_close_table (struct route_table *table) { struct route_node *rn; + rib_table_info_t *info = table->info; struct rib *rib; if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { - if (! RIB_SYSTEM_ROUTE (rib) - && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - rib_uninstall_kernel (rn, rib); + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + continue; + + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, NULL); + + if (! RIB_SYSTEM_ROUTE (rib)) + rib_uninstall_kernel (rn, rib); } } @@ -2980,7 +3397,7 @@ rib_close (void) rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); } - + /* Routing information base initialize. */ void rib_init (void) @@ -2989,3 +3406,106 @@ rib_init (void) /* VRF initialization. */ vrf_init (); } + +/* + * vrf_id_get_next + * + * Get the first vrf id that is greater than the given vrf id if any. + * + * Returns TRUE if a vrf id was found, FALSE otherwise. + */ +static inline int +vrf_id_get_next (uint32_t id, uint32_t *next_id_p) +{ + while (++id < vector_active (vrf_vector)) + { + if (vrf_lookup (id)) + { + *next_id_p = id; + return 1; + } + } + + return 0; +} + +/* + * rib_tables_iter_next + * + * Returns the next table in the iteration. + */ +struct route_table * +rib_tables_iter_next (rib_tables_iter_t *iter) +{ + struct route_table *table; + + /* + * Array that helps us go over all AFI/SAFI combinations via one + * index. + */ + static struct { + afi_t afi; + safi_t safi; + } afi_safis[] = { + { AFI_IP, SAFI_UNICAST }, + { AFI_IP, SAFI_MULTICAST }, + { AFI_IP6, SAFI_UNICAST }, + { AFI_IP6, SAFI_MULTICAST }, + }; + + table = NULL; + + switch (iter->state) + { + + case RIB_TABLES_ITER_S_INIT: + iter->vrf_id = 0; + iter->afi_safi_ix = -1; + + /* Fall through */ + + case RIB_TABLES_ITER_S_ITERATING: + iter->afi_safi_ix++; + while (1) + { + + while (iter->afi_safi_ix < (int) ZEBRA_NUM_OF (afi_safis)) + { + table = vrf_table (afi_safis[iter->afi_safi_ix].afi, + afi_safis[iter->afi_safi_ix].safi, + iter->vrf_id); + if (table) + break; + + iter->afi_safi_ix++; + } + + /* + * Found another table in this vrf. + */ + if (table) + break; + + /* + * Done with all tables in the current vrf, go to the next + * one. + */ + if (!vrf_id_get_next (iter->vrf_id, &iter->vrf_id)) + break; + + iter->afi_safi_ix = 0; + } + + break; + + case RIB_TABLES_ITER_S_DONE: + return NULL; + } + + if (table) + iter->state = RIB_TABLES_ITER_S_ITERATING; + else + iter->state = RIB_TABLES_ITER_S_DONE; + + return table; +} diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index b3111b8e..b0dca088 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -123,7 +123,7 @@ zebra_route_set_delete (struct vty *vty, struct route_map_index *index, return CMD_SUCCESS; } - + /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -422,14 +422,10 @@ route_match_ip_next_hop (void *rule, struct prefix *prefix, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: + /* Interface routes can't match ip next-hop */ + return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->rtype != NEXTHOP_TYPE_IPV4) - return RMAP_NOMATCH; - p.family = AF_INET; - p.prefix = nexthop->rgate.ipv4; - p.prefixlen = IPV4_MAX_BITLEN; - break; case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = nexthop->gate.ipv4; @@ -471,7 +467,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -488,14 +484,10 @@ route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: + /* Interface routes can't match ip next-hop */ + return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->rtype != NEXTHOP_TYPE_IPV4) - return RMAP_NOMATCH; - p.family = AF_INET; - p.prefix = nexthop->rgate.ipv4; - p.prefixlen = IPV4_MAX_BITLEN; - break; case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = nexthop->gate.ipv4; @@ -533,7 +525,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return @@ -579,7 +571,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -620,7 +612,7 @@ static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = route_match_ip_address_prefix_list_free }; - + /* `set src A.B.C.D' */ /* Set src. */ diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index f52bbcb8..f0a2e9fd 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -34,7 +34,7 @@ #include "zebra/rib.h" #include "zebra/zserv.h" - + #define IPFWMIB 1,3,6,1,2,1,4,24 /* ipForwardTable */ @@ -78,7 +78,7 @@ #define ROWSTATUS ASN_INTEGER #define IPADDRESS ASN_IPADDRESS #define OBJECTIDENTIFIER ASN_OBJECT_ID - + extern struct zebra_t zebrad; oid ipfw_oid [] = { IPFWMIB }; @@ -130,7 +130,7 @@ struct variable zebra_variables[] = {IPCIDRROUTESTATUS, ROWSTATUS, RONLY, ipCidrTable, 3, {4, 1, 16}} }; - + static u_char * ipFwNumber (struct variable *v, oid objid[], size_t *objid_len, int exact, size_t *val_len, WriteMethod **write_method) @@ -150,7 +150,7 @@ ipFwNumber (struct variable *v, oid objid[], size_t *objid_len, /* Return number of routing entries. */ result = 0; for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) result++; return (u_char *)&result; @@ -175,7 +175,7 @@ ipCidrNumber (struct variable *v, oid objid[], size_t *objid_len, /* Return number of routing entries. */ result = 0; for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) result++; return (u_char *)&result; @@ -245,6 +245,12 @@ proto_trans(int type) return 1; /* shouldn't happen */ case ZEBRA_ROUTE_BGP: return 14; /* bgp */ + case ZEBRA_ROUTE_HSLS: + return 1; /* other */ + case ZEBRA_ROUTE_OLSR: + return 1; /* other */ + case ZEBRA_ROUTE_BATMAN: + return 1; /* other */ default: return 1; /* other */ } @@ -369,7 +375,7 @@ get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, { if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest)) { - for (*rib = (*np)->info; *rib; *rib = (*rib)->next) + RNODE_FOREACH_RIB (*np, *rib) { if (!in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4, (u_char *)&nexthop)) @@ -388,12 +394,12 @@ get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, /* Check destination first */ if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) > 0) - for (rib2 = np2->info; rib2; rib2 = rib2->next) + RNODE_FOREACH_RIB (np2, rib2) check_replace(np2, rib2, np, rib); if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) == 0) { /* have to look at each rib individually */ - for (rib2 = np2->info; rib2; rib2 = rib2->next) + RNODE_FOREACH_RIB (np2, rib2) { int proto2, policy2; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 743c13fe..5ed03bf1 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -30,11 +30,16 @@ #include "zebra/zserv.h" -/* General fucntion for static route. */ +static int do_show_ip_route(struct vty *vty, safi_t safi); +static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, + int mcast); + +/* General function for static route. */ static int -zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, - const char *mask_str, const char *gate_str, - const char *flag_str, const char *distance_str) +zebra_static_ipv4_safi (struct vty *vty, safi_t safi, int add_cmd, + const char *dest_str, const char *mask_str, + const char *gate_str, const char *flag_str, + const char *distance_str) { int ret; u_char distance; @@ -81,9 +86,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, return CMD_WARNING; } if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -107,9 +112,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, if (gate_str == NULL) { if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, flag, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, flag, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -123,9 +128,168 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, ifname = gate_str; if (add_cmd) - static_add_ipv4 (&p, ifname ? NULL : &gate, ifname, flag, distance, 0); + static_add_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, flag, distance, 0); + else + static_delete_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, distance, 0); + + return CMD_SUCCESS; +} + +static int +zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, + const char *mask_str, const char *gate_str, + const char *flag_str, const char *distance_str) +{ + return zebra_static_ipv4_safi(vty, SAFI_UNICAST, add_cmd, dest_str, mask_str, gate_str, flag_str, distance_str); +} + +/* Static unicast routes for multicast RPF lookup. */ +DEFUN (ip_mroute_dist, + ip_mroute_dist_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + VTY_WARN_EXPERIMENTAL(); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); +} + +ALIAS (ip_mroute_dist, + ip_mroute_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + +DEFUN (no_ip_mroute_dist, + no_ip_mroute_dist_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + VTY_WARN_EXPERIMENTAL(); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); +} + +ALIAS (no_ip_mroute_dist, + no_ip_mroute_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + NO_STR + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + +DEFUN (ip_multicast_mode, + ip_multicast_mode_cmd, + "ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + VTY_WARN_EXPERIMENTAL(); + + if (!strncmp (argv[0], "u", 1)) + multicast_mode_ipv4_set (MCAST_URIB_ONLY); + else if (!strncmp (argv[0], "mrib-o", 6)) + multicast_mode_ipv4_set (MCAST_MRIB_ONLY); + else if (!strncmp (argv[0], "mrib-t", 6)) + multicast_mode_ipv4_set (MCAST_MIX_MRIB_FIRST); + else if (!strncmp (argv[0], "low", 3)) + multicast_mode_ipv4_set (MCAST_MIX_DISTANCE); + else if (!strncmp (argv[0], "lon", 3)) + multicast_mode_ipv4_set (MCAST_MIX_PFXLEN); + else + { + vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_multicast_mode, + no_ip_multicast_mode_cmd, + "no ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + multicast_mode_ipv4_set (MCAST_NO_CONFIG); + return CMD_SUCCESS; +} + +ALIAS (no_ip_multicast_mode, + no_ip_multicast_mode_noarg_cmd, + "no ip multicast rpf-lookup-mode", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n") + +DEFUN (show_ip_rpf, + show_ip_rpf_cmd, + "show ip rpf", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n") +{ + VTY_WARN_EXPERIMENTAL(); + return do_show_ip_route(vty, SAFI_MULTICAST); +} + +DEFUN (show_ip_rpf_addr, + show_ip_rpf_addr_cmd, + "show ip rpf A.B.C.D", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + "IP multicast source address (e.g. 10.0.0.0)\n") +{ + struct in_addr addr; + struct route_node *rn; + struct rib *rib; + int ret; + + VTY_WARN_EXPERIMENTAL(); + + ret = inet_aton (argv[0], &addr); + if (ret == 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rib = rib_match_ipv4_multicast (addr, &rn); + + if (rib) + vty_show_ip_route_detail (vty, rn, 1); else - static_delete_ipv4 (&p, ifname ? NULL : &gate, ifname, distance, 0); + vty_out (vty, "%% No match for RPF lookup%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -280,9 +444,9 @@ DEFUN (ip_route_mask_flags_distance, "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Distance value for this route\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Distance value for this route\n") { return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4]); } @@ -294,9 +458,9 @@ DEFUN (ip_route_mask_flags_distance2, "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" - "Distance value for this route\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Distance value for this route\n") { return zebra_static_ipv4 (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3]); } @@ -530,15 +694,24 @@ DEFUN (no_ip_protocol, /* New RIB. Detailed information for IPv4 route. */ static void -vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) +vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) { struct rib *rib; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { - vty_out (vty, "Routing entry for %s/%d%s", - inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + const char *mcast_info = ""; + if (mcast) + { + rib_table_info_t *info = rn->table->info; + mcast_info = (info->safi == SAFI_MULTICAST) + ? " using Multicast RIB" + : " using Unicast RIB"; + } + vty_out (vty, "Routing entry for %s/%d%s%s", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, mcast_info, VTY_NEWLINE); vty_out (vty, " Known via \"%s\"", zebra_route_string (rib->type)); vty_out (vty, ", distance %u, metric %u", rib->distance, rib->metric); @@ -558,7 +731,10 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -582,12 +758,13 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " ago%s", VTY_NEWLINE); } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { char addrstr[32]; - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); + vty_out (vty, " %c%s", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', + recursing ? " " : ""); switch (nexthop->type) { @@ -613,25 +790,12 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -668,12 +832,13 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) static void vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (nexthop == rib->nexthop) { @@ -697,7 +862,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', - len - 3, ' '); + len - 3 + (2 * recursing), ' '); switch (nexthop->type) { @@ -723,25 +888,12 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -777,7 +929,10 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -811,18 +966,22 @@ DEFUN (show_ip_route, IP_STR "IP routing table\n") { + return do_show_ip_route(vty, SAFI_UNICAST); +} + +static int do_show_ip_route(struct vty *vty, safi_t safi) { struct route_table *table; struct route_node *rn; struct rib *rib; int first = 1; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return CMD_SUCCESS; /* Show all IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -863,7 +1022,7 @@ DEFUN (show_ip_route_prefix_longer, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (prefix_match (&p, &rn->p)) { if (first) @@ -896,7 +1055,7 @@ DEFUN (show_ip_route_supernets, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { addr = ntohl (rn->p.u.prefix4.s_addr); @@ -942,7 +1101,7 @@ DEFUN (show_ip_route_protocol, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (rib->type == type) { if (first) @@ -986,7 +1145,7 @@ DEFUN (show_ip_route_addr, return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -1021,10 +1180,12 @@ DEFUN (show_ip_route_prefix, if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + if (rn) + route_unlock_node (rn); return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -1046,12 +1207,13 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) memset (&rib_cnt, 0, sizeof(rib_cnt)); memset (&fib_cnt, 0, sizeof(fib_cnt)); for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { rib_cnt[ZEBRA_ROUTE_TOTAL]++; rib_cnt[rib->type]++; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { fib_cnt[ZEBRA_ROUTE_TOTAL]++; fib_cnt[rib->type]++; @@ -1060,7 +1222,8 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) { rib_cnt[ZEBRA_ROUTE_IBGP]++; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) fib_cnt[ZEBRA_ROUTE_IBGP]++; } } @@ -1093,6 +1256,84 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE); } +/* + * Implementation of the ip route summary prefix command. + * + * This command prints the primary prefixes that have been installed by various + * protocols on the box. + * + */ +static void +vty_show_ip_route_summary_prefix (struct vty *vty, struct route_table *table) +{ + struct route_node *rn; + struct rib *rib; + struct nexthop *nexthop; +#define ZEBRA_ROUTE_IBGP ZEBRA_ROUTE_MAX +#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1) + u_int32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1]; + u_int32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; + u_int32_t i; + int cnt; + + memset (&rib_cnt, 0, sizeof(rib_cnt)); + memset (&fib_cnt, 0, sizeof(fib_cnt)); + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + + /* + * In case of ECMP, count only once. + */ + cnt = 0; + for (nexthop = rib->nexthop; (!cnt && nexthop); nexthop = nexthop->next) + { + cnt++; + rib_cnt[ZEBRA_ROUTE_TOTAL]++; + rib_cnt[rib->type]++; + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + fib_cnt[ZEBRA_ROUTE_TOTAL]++; + fib_cnt[rib->type]++; + } + if (rib->type == ZEBRA_ROUTE_BGP && + CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + { + rib_cnt[ZEBRA_ROUTE_IBGP]++; + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + fib_cnt[ZEBRA_ROUTE_IBGP]++; + } + } + } + + vty_out (vty, "%-20s %-20s %-20s %s", + "Route Source", "Prefix Routes", "FIB", VTY_NEWLINE); + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (rib_cnt[i] > 0) + { + if (i == ZEBRA_ROUTE_BGP) + { + vty_out (vty, "%-20s %-20d %-20d %s", "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP] - rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_BGP] - fib_cnt[ZEBRA_ROUTE_IBGP], + VTY_NEWLINE); + vty_out (vty, "%-20s %-20d %-20d %s", "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], fib_cnt[ZEBRA_ROUTE_IBGP], + VTY_NEWLINE); + } + else + vty_out (vty, "%-20s %-20d %-20d %s", zebra_route_string(i), + rib_cnt[i], fib_cnt[i], VTY_NEWLINE); + } + } + + vty_out (vty, "------%s", VTY_NEWLINE); + vty_out (vty, "%-20s %-20d %-20d %s", "Totals", rib_cnt[ZEBRA_ROUTE_TOTAL], + fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE); +} + /* Show route summary. */ DEFUN (show_ip_route_summary, show_ip_route_summary_cmd, @@ -1113,9 +1354,30 @@ DEFUN (show_ip_route_summary, return CMD_SUCCESS; } +/* Show route summary prefix. */ +DEFUN (show_ip_route_summary_prefix, + show_ip_route_summary_prefix_cmd, + "show ip route summary prefix", + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + "Prefix routes\n") +{ + struct route_table *table; + + table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary_prefix (vty, table); + + return CMD_SUCCESS; +} + /* Write IPv4 static route configuration. */ static int -static_config_ipv4 (struct vty *vty) +static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) { struct route_node *rn; struct static_ipv4 *si; @@ -1125,14 +1387,14 @@ static_config_ipv4 (struct vty *vty) write = 0; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, 0); + stable = vrf_static_table (AFI_IP, safi, 0); if (! stable) return -1; for (rn = route_top (stable); rn; rn = route_next (rn)) for (si = rn->info; si; si = si->next) { - vty_out (vty, "ip route %s/%d", inet_ntoa (rn->p.u.prefix4), + vty_out (vty, "%s %s/%d", cmd, inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); switch (si->type) @@ -1219,7 +1481,7 @@ DEFUN (show_ip_mroute, /* Show all IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -1231,7 +1493,7 @@ DEFUN (show_ip_mroute, return CMD_SUCCESS; } - + #ifdef HAVE_IPV6 /* General fucntion for IPv6 static route. */ static int @@ -1543,10 +1805,11 @@ static void vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) { struct rib *rib; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf[BUFSIZ]; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { vty_out (vty, "Routing entry for %s/%d%s", inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), @@ -1570,7 +1833,10 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -1594,10 +1860,11 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " ago%s", VTY_NEWLINE); } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); + vty_out (vty, " %c%s", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', + recursing ? " " : ""); switch (nexthop->type) { @@ -1625,30 +1892,12 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " via %s)", - inet_ntop (AF_INET6, &nexthop->rgate.ipv6, - buf, BUFSIZ)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); @@ -1659,12 +1908,13 @@ static void vty_show_ipv6_route (struct vty *vty, struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (nexthop == rib->nexthop) { @@ -1688,7 +1938,7 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', - len - 3, ' '); + len - 3 + (2 * recursing), ' '); switch (nexthop->type) { @@ -1717,29 +1967,7 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " via %s)", - inet_ntop (AF_INET6, &nexthop->rgate.ipv6, - buf, BUFSIZ)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) vty_out (vty, ", bh"); @@ -1750,7 +1978,10 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -1795,7 +2026,7 @@ DEFUN (show_ipv6_route, /* Show all IPv6 route. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -1836,7 +2067,7 @@ DEFUN (show_ipv6_route_prefix_longer, /* Show matched type IPv6 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (prefix_match (&p, &rn->p)) { if (first) @@ -1876,7 +2107,7 @@ DEFUN (show_ipv6_route_protocol, /* Show matched type IPv6 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (rib->type == type) { if (first) @@ -1955,6 +2186,8 @@ DEFUN (show_ipv6_route_prefix, if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + if (rn) + route_unlock_node (rn); return CMD_WARNING; } @@ -1985,6 +2218,27 @@ DEFUN (show_ipv6_route_summary, return CMD_SUCCESS; } +/* Show ipv6 route summary prefix. */ +DEFUN (show_ipv6_route_summary_prefix, + show_ipv6_route_summary_prefix_cmd, + "show ipv6 route summary prefix", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + "Prefix routes\n") +{ + struct route_table *table; + + table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary_prefix (vty, table); + + return CMD_SUCCESS; +} + /* * Show IPv6 mroute command.Used to dump * the Multicast routing table. @@ -2008,7 +2262,7 @@ DEFUN (show_ipv6_mroute, /* Show all IPv6 route. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -2080,7 +2334,8 @@ zebra_ip_config (struct vty *vty) { int write = 0; - write += static_config_ipv4 (vty); + write += static_config_ipv4 (vty, SAFI_UNICAST, "ip route"); + write += static_config_ipv4 (vty, SAFI_MULTICAST, "ip mroute"); #ifdef HAVE_IPV6 write += static_config_ipv6 (vty); #endif /* HAVE_IPV6 */ @@ -2088,10 +2343,19 @@ zebra_ip_config (struct vty *vty) return write; } -/* ip protocol configuration write function */ -static int config_write_protocol(struct vty *vty) -{ +static int config_write_vty(struct vty *vty) +{ int i; + enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get (); + + if (ipv4_multicast_mode != MCAST_NO_CONFIG) + vty_out (vty, "ip multicast rpf-lookup-mode %s%s", + ipv4_multicast_mode == MCAST_URIB_ONLY ? "urib-only" : + ipv4_multicast_mode == MCAST_MRIB_ONLY ? "mrib-only" : + ipv4_multicast_mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib" : + ipv4_multicast_mode == MCAST_MIX_DISTANCE ? "lower-distance" : + "longer-prefix", + VTY_NEWLINE); for (i=0;imtu6); stream_putl (s, ifp->bandwidth); #ifdef HAVE_STRUCT_SOCKADDR_DL - stream_put (s, &ifp->sdl, sizeof (ifp->sdl)); + stream_put (s, &ifp->sdl, sizeof (ifp->sdl_storage)); #else stream_putl (s, ifp->hw_addr_len); if (ifp->hw_addr_len) @@ -389,7 +389,8 @@ zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -488,6 +489,9 @@ zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) num = 0; nump = stream_get_endp(s); stream_putc (s, 0); + /* Only non-recursive routes are elegible to resolve nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { @@ -535,8 +539,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) u_char num; struct nexthop *nexthop; - /* Lookup nexthop. */ - rib = rib_match_ipv4 (addr); + /* Lookup nexthop - eBGP excluded */ + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL); /* Get output stream. */ s = client->obuf; @@ -548,10 +552,15 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) if (rib) { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Matching rib entry found.", __func__); stream_putl (s, rib->metric); num = 0; nump = stream_get_endp(s); stream_putc (s, 0); + /* Only non-recursive routes are elegible to resolve the nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { @@ -561,6 +570,10 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) case ZEBRA_NEXTHOP_IPV4: stream_put_in_addr (s, &nexthop->gate.ipv4); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: stream_putl (s, nexthop->ifindex); @@ -575,6 +588,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) } else { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: No matching rib entry found.", __func__); stream_putl (s, 0); stream_putc (s, 0); } @@ -584,6 +599,76 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) return zebra_server_send_message(client); } +/* + Modified version of zsend_ipv4_nexthop_lookup(): + Query unicast rib if nexthop is not found on mrib. + Returns both route metric and protocol distance. +*/ +static int +zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr, + struct rib *rib) +{ + struct stream *s; + unsigned long nump; + u_char num; + struct nexthop *nexthop; + + /* Get output stream. */ + s = client->obuf; + stream_reset (s); + + /* Fill in result. */ + zserv_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB); + stream_put_in_addr (s, &addr); + + if (rib) + { + stream_putc (s, rib->distance); + stream_putl (s, rib->metric); + num = 0; + nump = stream_get_endp(s); /* remember position for nexthop_num */ + stream_putc (s, 0); /* reserve room for nexthop_num */ + /* Only non-recursive routes are elegible to resolve the nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + stream_putc (s, nexthop->type); + switch (nexthop->type) + { + case ZEBRA_NEXTHOP_IPV4: + stream_put_in_addr (s, &nexthop->gate.ipv4); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + stream_putl (s, nexthop->ifindex); + break; + default: + /* do nothing */ + break; + } + num++; + } + + stream_putc_at (s, nump, num); /* store nexthop_num */ + } + else + { + stream_putc (s, 0); /* distance */ + stream_putl (s, 0); /* metric */ + stream_putc (s, 0); /* nexthop_num */ + } + + stream_putw_at (s, 0, stream_get_endp (s)); + + return zebra_server_send_message(client); +} + static int zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) { @@ -611,7 +696,8 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) nump = stream_get_endp(s); stream_putc (s, 0); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { stream_putc (s, nexthop->type); switch (nexthop->type) @@ -619,6 +705,10 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) case ZEBRA_NEXTHOP_IPV4: stream_put_in_addr (s, &nexthop->gate.ipv4); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: stream_putl (s, nexthop->ifindex); @@ -641,7 +731,7 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) return zebra_server_send_message(client); } - + /* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ int zsend_router_id_update (struct zserv *client, struct prefix *p) @@ -670,7 +760,7 @@ zsend_router_id_update (struct zserv *client, struct prefix *p) return zebra_server_send_message(client); } - + /* Register zebra server interface information. Send current all interface and address information. */ static int @@ -859,6 +949,7 @@ zread_ipv4_delete (struct zserv *client, u_short length) break; case ZEBRA_NEXTHOP_IPV4_IFINDEX: nexthop.s_addr = stream_get_ipv4 (s); + nexthop_p = &nexthop; ifindex = stream_getl (s); break; case ZEBRA_NEXTHOP_IPV6: @@ -890,11 +981,27 @@ static int zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) { struct in_addr addr; + char buf[BUFSIZ]; addr.s_addr = stream_get_ipv4 (client->ibuf); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: looking up %s", __func__, + inet_ntop (AF_INET, &addr, buf, BUFSIZ)); return zsend_ipv4_nexthop_lookup (client, addr); } +/* MRIB Nexthop lookup for IPv4. */ +static int +zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length) +{ + struct in_addr addr; + struct rib *rib; + + addr.s_addr = stream_get_ipv4 (client->ibuf); + rib = rib_match_ipv4_multicast (addr, NULL); + return zsend_ipv4_nexthop_lookup_mrib (client, addr, rib); +} + /* Nexthop lookup for IPv4. */ static int zread_ipv4_import_lookup (struct zserv *client, u_short length) @@ -1049,7 +1156,9 @@ zread_ipv6_nexthop_lookup (struct zserv *client, u_short length) char buf[BUFSIZ]; stream_get (&addr, client->ibuf, 16); - printf ("DEBUG %s\n", inet_ntop (AF_INET6, &addr, buf, BUFSIZ)); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: looking up %s", __func__, + inet_ntop (AF_INET6, &addr, buf, BUFSIZ)); return zsend_ipv6_nexthop_lookup (client, &addr); } @@ -1325,6 +1434,9 @@ zebra_client_read (struct thread *thread) case ZEBRA_IPV4_NEXTHOP_LOOKUP: zread_ipv4_nexthop_lookup (client, length); break; + case ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB: + zread_ipv4_nexthop_lookup_mrib (client, length); + break; #ifdef HAVE_IPV6 case ZEBRA_IPV6_NEXTHOP_LOOKUP: zread_ipv6_nexthop_lookup (client, length); @@ -1512,7 +1624,7 @@ zebra_serv_un (const char *path) zebra_event (ZEBRA_SERV, sock, NULL); } - + static void zebra_event (enum event event, int sock, struct zserv *client) @@ -1531,7 +1643,7 @@ zebra_event (enum event event, int sock, struct zserv *client) break; } } - + /* Display default rtm_table for all clients. */ DEFUN (show_table, show_table_cmd, @@ -1631,7 +1743,7 @@ static struct cmd_node table_node = "", /* This node has no interface. */ 1 }; - + /* Only display ip forwarding is enabled or not. */ DEFUN (show_ip_forwarding, show_ip_forwarding_cmd, @@ -1752,7 +1864,7 @@ static struct cmd_node forwarding_node = 1 }; - + /* Initialisation of zebra and installation of commands. */ void zebra_init (void)